Browse Source

Modif dans le parsing IFC suppressions des espaces avant traitement de la chaine

guillaume 11 years ago
parent
commit
8267d93537
5 changed files with 1218 additions and 1254 deletions
  1. 963 977
      code/IFCLoader.cpp
  2. 238 242
      code/LineSplitter.h
  3. 13 13
      code/STEPFile.h
  4. 4 18
      code/STEPFileReader.cpp
  5. 0 4
      code/STEPFileReader.h

+ 963 - 977
code/IFCLoader.cpp

@@ -1,979 +1,965 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2012, assimp team
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms, 
-with or without modification, are permitted provided that the 
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-
-/** @file  IFCLoad.cpp
- *  @brief Implementation of the Industry Foundation Classes loader.
- */
-#include "AssimpPCH.h"
-
-#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
-
-#include <iterator>
-#include <boost/tuple/tuple.hpp>
-
-#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
-#	include "../contrib/unzip/unzip.h"
-#endif
-
-#include "IFCLoader.h"
-#include "STEPFileReader.h"
-
-#include "IFCUtil.h"
-
-#include "StreamReader.h"
-#include "MemoryIOWrapper.h"
-
-namespace Assimp {
-	template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
-}
-
-using namespace Assimp;
-using namespace Assimp::Formatter;
-using namespace Assimp::IFC;
-
-/* DO NOT REMOVE this comment block. The genentitylist.sh script
- * just looks for names adhering to the IfcSomething naming scheme
- * and includes all matches in the whitelist for code-generation. Thus,
- * all entity classes that are only indirectly referenced need to be
- * mentioned explicitly.
-
-  IfcRepresentationMap
-  IfcProductRepresentation
-  IfcUnitAssignment
-  IfcClosedShell
-  IfcDoor
-
- */
-
-namespace {
-
-
-// forward declarations
-void SetUnits(ConversionData& conv);
-void SetCoordinateSpace(ConversionData& conv);
-void ProcessSpatialStructures(ConversionData& conv);
-aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el ,ConversionData& conv);
-void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, ConversionData& conv);
-void MakeTreeRelative(ConversionData& conv);
-void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv);
-
-} // anon
-
-static const aiImporterDesc desc = {
-	"Industry Foundation Classes (IFC) Importer",
-	"",
-	"",
-	"",
-	aiImporterFlags_SupportBinaryFlavour,
-	0,
-	0,
-	0,
-	0,
-	"ifc ifczip" 
-};
-
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-IFCImporter::IFCImporter()
-{}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well 
-IFCImporter::~IFCImporter()
-{
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns whether the class can handle the format of the given file. 
-bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
-	const std::string& extension = GetExtension(pFile);
-	if (extension == "ifc" || extension == "ifczip") {
-		return true;
-	}
-
-	else if ((!extension.length() || checkSig) && pIOHandler)	{
-		// note: this is the common identification for STEP-encoded files, so
-		// it is only unambiguous as long as we don't support any further
-		// file formats with STEP as their encoding.
-		const char* tokens[] = {"ISO-10303-21"};
-		return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
-	}
-	return false;
-}
-
-// ------------------------------------------------------------------------------------------------
-// List all extensions handled by this loader
-const aiImporterDesc* IFCImporter::GetInfo () const
-{
-	return &desc;
-}
-
-
-// ------------------------------------------------------------------------------------------------
-// Setup configuration properties for the loader
-void IFCImporter::SetupProperties(const Importer* pImp)
-{
-	settings.skipSpaceRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS,true);
-	settings.skipCurveRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_CURVE_REPRESENTATIONS,true);
-	settings.useCustomTriangulation = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION,true);
-
-	settings.conicSamplingAngle = 10.f;
-	settings.skipAnnotations = true;
-}
-
-
-// ------------------------------------------------------------------------------------------------
-// Imports the given file into the given scene structure. 
-void IFCImporter::InternReadFile( const std::string& pFile, 
-	aiScene* pScene, IOSystem* pIOHandler)
-{
-	boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile));
-	if (!stream) {
-		ThrowException("Could not open file for reading");
-	}
-
-
-	// if this is a ifczip file, decompress its contents first
-	if(GetExtension(pFile) == "ifczip") {
-#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
-		unzFile zip = unzOpen( pFile.c_str() );
-		if(zip == NULL) {
-			ThrowException("Could not open ifczip file for reading, unzip failed");
-		}
-
-		// chop 'zip' postfix
-		std::string fileName = pFile.substr(0,pFile.length() - 3);
-
-		std::string::size_type s = pFile.find_last_of('\\');
-		if(s == std::string::npos) {
-			s = pFile.find_last_of('/');
-		}
-		if(s != std::string::npos) {
-			fileName = fileName.substr(s+1);
-		}
-
-		// search file (same name as the IFCZIP except for the file extension) and place file pointer there
-		
-		if(UNZ_OK == unzGoToFirstFile(zip)) {
-			do {
-				//
-
-				// get file size, etc.
-				unz_file_info fileInfo;
-				char filename[256];
-				unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 );
-				
-				if (GetExtension(filename) != "ifc") {
-					continue;
-				}
-
-				uint8_t* buff = new uint8_t[fileInfo.uncompressed_size];
-
-				LogInfo("Decompressing IFCZIP file");
-
-				unzOpenCurrentFile( zip  );
-				const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size);
-				size_t filesize = fileInfo.uncompressed_size;
-				if ( ret < 0 || size_t(ret) != filesize )
-				{
-					delete[] buff;
-					ThrowException("Failed to decompress IFC ZIP file");
-				}
-				unzCloseCurrentFile( zip );
-				stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true));
-				break;
-
-				if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) {
-					ThrowException("Found no IFC file member in IFCZIP file (1)");
-				}
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  IFCLoad.cpp
+ *  @brief Implementation of the Industry Foundation Classes loader.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
+
+#include <iterator>
+#include <boost/tuple/tuple.hpp>
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
+#	include "../contrib/unzip/unzip.h"
+#endif
+
+#include "IFCLoader.h"
+#include "STEPFileReader.h"
+
+#include "IFCUtil.h"
+
+#include "StreamReader.h"
+#include "MemoryIOWrapper.h"
+
+namespace Assimp {
+	template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Formatter;
+using namespace Assimp::IFC;
+
+/* DO NOT REMOVE this comment block. The genentitylist.sh script
+ * just looks for names adhering to the IfcSomething naming scheme
+ * and includes all matches in the whitelist for code-generation. Thus,
+ * all entity classes that are only indirectly referenced need to be
+ * mentioned explicitly.
+
+  IfcRepresentationMap
+  IfcProductRepresentation
+  IfcUnitAssignment
+  IfcClosedShell
+  IfcDoor
+
+ */
+
+namespace {
+
+
+// forward declarations
+void SetUnits(ConversionData& conv);
+void SetCoordinateSpace(ConversionData& conv);
+void ProcessSpatialStructures(ConversionData& conv);
+aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el ,ConversionData& conv);
+void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, ConversionData& conv);
+void MakeTreeRelative(ConversionData& conv);
+void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv);
+
+} // anon
+
+static const aiImporterDesc desc = {
+	"Industry Foundation Classes (IFC) Importer",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	0,
+	0,
+	"ifc ifczip" 
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IFCImporter::IFCImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+IFCImporter::~IFCImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+	const std::string& extension = GetExtension(pFile);
+	if (extension == "ifc" || extension == "ifczip") {
+		return true;
+	}
+
+	else if ((!extension.length() || checkSig) && pIOHandler)	{
+		// note: this is the common identification for STEP-encoded files, so
+		// it is only unambiguous as long as we don't support any further
+		// file formats with STEP as their encoding.
+		const char* tokens[] = {"ISO-10303-21"};
+		return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+	}
+	return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+const aiImporterDesc* IFCImporter::GetInfo () const
+{
+	return &desc;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void IFCImporter::SetupProperties(const Importer* pImp)
+{
+	settings.skipSpaceRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS,true);
+	settings.skipCurveRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_CURVE_REPRESENTATIONS,true);
+	settings.useCustomTriangulation = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION,true);
+
+	settings.conicSamplingAngle = 10.f;
+	settings.skipAnnotations = true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void IFCImporter::InternReadFile( const std::string& pFile, 
+	aiScene* pScene, IOSystem* pIOHandler)
+{
+	boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile));
+	if (!stream) {
+		ThrowException("Could not open file for reading");
+	}
+
+
+	// if this is a ifczip file, decompress its contents first
+	if(GetExtension(pFile) == "ifczip") {
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
+		unzFile zip = unzOpen( pFile.c_str() );
+		if(zip == NULL) {
+			ThrowException("Could not open ifczip file for reading, unzip failed");
+		}
+
+		// chop 'zip' postfix
+		std::string fileName = pFile.substr(0,pFile.length() - 3);
+
+		std::string::size_type s = pFile.find_last_of('\\');
+		if(s == std::string::npos) {
+			s = pFile.find_last_of('/');
+		}
+		if(s != std::string::npos) {
+			fileName = fileName.substr(s+1);
+		}
+
+		// search file (same name as the IFCZIP except for the file extension) and place file pointer there
+		if(UNZ_OK == unzGoToFirstFile(zip)) {
+			do {
+				// get file size, etc.
+				unz_file_info fileInfo;
+				char filename[256];
+				unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 );
+				if (GetExtension(filename) != "ifc") {
+					continue;
+				}
+				uint8_t* buff = new uint8_t[fileInfo.uncompressed_size];
+				LogInfo("Decompressing IFCZIP file");
+				unzOpenCurrentFile( zip  );
+				const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size);
+				size_t filesize = fileInfo.uncompressed_size;
+				if ( ret < 0 || size_t(ret) != filesize )
+				{
+					delete[] buff;
+					ThrowException("Failed to decompress IFC ZIP file");
+				}
+				unzCloseCurrentFile( zip );
+				stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true));
+				break;
+
+				if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) {
+					ThrowException("Found no IFC file member in IFCZIP file (1)");
+				}
 
 
 			} while(true);
 			} while(true);
-		}
-		else {
-			ThrowException("Found no IFC file member in IFCZIP file (2)");
-		}
-
-		unzClose(zip);
-#else
-		ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support");
-#endif
-	}
-
-	boost::scoped_ptr<STEP::DB> db(STEP::ReadFileHeader(stream));
-	const STEP::HeaderInfo& head = static_cast<const STEP::DB&>(*db).GetHeader();
-
-	if(!head.fileSchema.size() || head.fileSchema.substr(0,3) != "IFC") {
-		ThrowException("Unrecognized file schema: " + head.fileSchema);
-	}
-
-	if (!DefaultLogger::isNullLogger()) {
-		LogDebug("File schema is \'" + head.fileSchema + '\'');
-		if (head.timestamp.length()) {
-			LogDebug("Timestamp \'" + head.timestamp + '\'');
-		}
-		if (head.app.length()) {
-			LogDebug("Application/Exporter identline is \'" + head.app  + '\'');
-		}
-	}
-
-	// obtain a copy of the machine-generated IFC scheme
-	EXPRESS::ConversionSchema schema;
-	GetSchema(schema);
-
-	// tell the reader which entity types to track with special care
-	static const char* const types_to_track[] = {
-		"ifcsite", "ifcbuilding", "ifcproject"
-	};
-
-	// tell the reader for which types we need to simulate STEPs reverse indices
-	static const char* const inverse_indices_to_track[] = {
-		"ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
-	};
-
-	// feed the IFC schema into the reader and pre-parse all lines
-	STEP::ReadFile(*db, schema, types_to_track, inverse_indices_to_track);
-
-	const STEP::LazyObject* proj =  db->GetObject("ifcproject");
-	if (!proj) {
-		ThrowException("missing IfcProject entity");
-	}
-
-	ConversionData conv(*db,proj->To<IfcProject>(),pScene,settings);
-	SetUnits(conv);
-	SetCoordinateSpace(conv);
-	ProcessSpatialStructures(conv);
-	MakeTreeRelative(conv);
-
-	// NOTE - this is a stress test for the importer, but it works only
-	// in a build with no entities disabled. See 
-	//     scripts/IFCImporter/CPPGenerator.py
-	// for more information.
-#ifdef ASSIMP_IFC_TEST
-	db->EvaluateAll();
-#endif
-
-	// do final data copying
-	if (conv.meshes.size()) {
-		pScene->mNumMeshes = static_cast<unsigned int>(conv.meshes.size());
-		pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
-		std::copy(conv.meshes.begin(),conv.meshes.end(),pScene->mMeshes);
-
-		// needed to keep the d'tor from burning us
-		conv.meshes.clear();
-	}
-
-	if (conv.materials.size()) {
-		pScene->mNumMaterials = static_cast<unsigned int>(conv.materials.size());
-		pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
-		std::copy(conv.materials.begin(),conv.materials.end(),pScene->mMaterials);
-
-		// needed to keep the d'tor from burning us
-		conv.materials.clear();
-	}
-
-	// apply world coordinate system (which includes the scaling to convert to meters and a -90 degrees rotation around x)
-	aiMatrix4x4 scale, rot;
-	aiMatrix4x4::Scaling(static_cast<aiVector3D>(IfcVector3(conv.len_scale)),scale);
-	aiMatrix4x4::RotationX(-AI_MATH_HALF_PI_F,rot);
-
-	pScene->mRootNode->mTransformation = rot * scale * conv.wcs * pScene->mRootNode->mTransformation;
-
-	// this must be last because objects are evaluated lazily as we process them
-	if ( !DefaultLogger::isNullLogger() ){
-		LogDebug((Formatter::format(),"STEP: evaluated ",db->GetEvaluatedObjectCount()," object records"));
-	}
-}
-
-namespace {
-
-
-// ------------------------------------------------------------------------------------------------
-void ConvertUnit(const IfcNamedUnit& unit,ConversionData& conv)
-{
-	if(const IfcSIUnit* const si = unit.ToPtr<IfcSIUnit>()) {
-
-		if(si->UnitType == "LENGTHUNIT") { 
-			conv.len_scale = si->Prefix ? ConvertSIPrefix(si->Prefix) : 1.f;
-			IFCImporter::LogDebug("got units used for lengths");
-		}
-		if(si->UnitType == "PLANEANGLEUNIT") { 
-			if (si->Name != "RADIAN") {
-				IFCImporter::LogWarn("expected base unit for angles to be radian");
-			}
-		}
-	}
-	else if(const IfcConversionBasedUnit* const convu = unit.ToPtr<IfcConversionBasedUnit>()) {
-
-		if(convu->UnitType == "PLANEANGLEUNIT") { 
-			try {
-				conv.angle_scale = convu->ConversionFactor->ValueComponent->To<EXPRESS::REAL>();
-				ConvertUnit(*convu->ConversionFactor->UnitComponent,conv);
-				IFCImporter::LogDebug("got units used for angles");
-			}
-			catch(std::bad_cast&) {
-				IFCImporter::LogError("skipping unknown IfcConversionBasedUnit.ValueComponent entry - expected REAL");
-			}
-		}
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv)
-{
-	try {
-		const EXPRESS::ENTITY& e = dt.To<ENTITY>();
-
-		const IfcNamedUnit& unit = e.ResolveSelect<IfcNamedUnit>(conv.db);
-		if(unit.UnitType != "LENGTHUNIT" && unit.UnitType != "PLANEANGLEUNIT") {
-			return;
-		}
-
-		ConvertUnit(unit,conv);
-	}
-	catch(std::bad_cast&) {
-		// not entity, somehow
-		IFCImporter::LogError("skipping unknown IfcUnit entry - expected entity");
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void SetUnits(ConversionData& conv)
-{
-	// see if we can determine the coordinate space used to express. 
-	for(size_t i = 0; i <  conv.proj.UnitsInContext->Units.size(); ++i ) {
-		ConvertUnit(*conv.proj.UnitsInContext->Units[i],conv);
-	}
-}
-
-
-// ------------------------------------------------------------------------------------------------
-void SetCoordinateSpace(ConversionData& conv)
-{
-	const IfcRepresentationContext* fav = NULL;
-	BOOST_FOREACH(const IfcRepresentationContext& v, conv.proj.RepresentationContexts) {
-		fav = &v;
-		// Model should be the most suitable type of context, hence ignore the others 
-		if (v.ContextType && v.ContextType.Get() == "Model") { 
-			break;
-		}
-	}
-	if (fav) {
-		if(const IfcGeometricRepresentationContext* const geo = fav->ToPtr<IfcGeometricRepresentationContext>()) {
-			ConvertAxisPlacement(conv.wcs, *geo->WorldCoordinateSystem, conv);
-			IFCImporter::LogDebug("got world coordinate system");
-		}
-	}
-}
-
-
-// ------------------------------------------------------------------------------------------------
-void ResolveObjectPlacement(aiMatrix4x4& m, const IfcObjectPlacement& place, ConversionData& conv)
-{
-	if (const IfcLocalPlacement* const local = place.ToPtr<IfcLocalPlacement>()){
-		IfcMatrix4 tmp;
-		ConvertAxisPlacement(tmp, *local->RelativePlacement, conv);
-
-		m = static_cast<aiMatrix4x4>(tmp);
-
-		if (local->PlacementRelTo) {
-			aiMatrix4x4 tmp;
-			ResolveObjectPlacement(tmp,local->PlacementRelTo.Get(),conv);
-			m = tmp * m;
-		}
-	}
-	else {
-		IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is " + place.GetClassName());
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
-{
-	aiMatrix4x4 t;
-	if (nd->mParent) {
-		GetAbsTransform(t,nd->mParent,conv);
-	}
-	out = t*nd->mTransformation;
-}
-
-// ------------------------------------------------------------------------------------------------
-bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
-{
-	// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
-	std::auto_ptr<aiNode> nd(new aiNode());
-	nd->mName.Set("IfcMappedItem");
-		
-	// handle the Cartesian operator
-	IfcMatrix4 m;
-	ConvertTransformOperator(m, *mapped.MappingTarget);
-
-	IfcMatrix4 msrc;
-	ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
-
-	msrc = m*msrc;
-
-	std::vector<unsigned int> meshes;
-	const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0;
-	if (conv.apply_openings) {
-		IfcMatrix4 minv = msrc;
-		minv.Inverse();
-		BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
-			open.Transform(minv);
-		}
-	}
-
-	const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
-
-	bool got = false;
-	BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
-		if(!ProcessRepresentationItem(item,meshes,conv)) {
-			IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
-		}
-		else got = true;
-	}
-
-	if (!got) {
-		return false;
-	}
-
-	AssignAddedMeshes(meshes,nd.get(),conv);
-	if (conv.collect_openings) {
-
-		// if this pass serves us only to collect opening geometry,
-		// make sure we transform the TempMesh's which we need to
-		// preserve as well.
-		if(const size_t diff = conv.collect_openings->size() - old_openings) {
-			for(size_t i = 0; i < diff; ++i) {
-				(*conv.collect_openings)[old_openings+i].Transform(msrc);
-			}
-		}
-	}
-
-	nd->mTransformation =  nd_src->mTransformation * static_cast<aiMatrix4x4>( msrc );
-	subnodes_src.push_back(nd.release());
-
-	return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-struct RateRepresentationPredicate {
-
-	int Rate(const IfcRepresentation* r) const {
-		// the smaller, the better
-
-		if (! r->RepresentationIdentifier) {
-			// neutral choice if no extra information is specified
-			return 0;
-		}
-
-		
-		const std::string& name = r->RepresentationIdentifier.Get();
-		if (name == "MappedRepresentation") {
-			if (!r->Items.empty()) {
-				// take the first item and base our choice on it
-				const IfcMappedItem* const m = r->Items.front()->ToPtr<IfcMappedItem>();
-				if (m) {
-					return Rate(m->MappingSource->MappedRepresentation);
-				}
-			}
-			return 100;
-		}
-
-		return Rate(name);
-	}
-
-	int Rate(const std::string& r) const {
-
-
-		if (r == "SolidModel") {
-			return -3;
-		}
-		
-		// give strong preference to extruded geometry.
-		if (r == "SweptSolid") {
-			return -10;
-		}
-		
-		if (r == "Clipping") {
-			return -5;
-		}
-
-		// 'Brep' is difficult to get right due to possible voids in the
-		// polygon boundaries, so take it only if we are forced to (i.e.
-		// if the only alternative is (non-clipping) boolean operations, 
-		// which are not supported at all).
-		if (r == "Brep") {
-			return -2;
-		}
-		
-		// Curves, bounding boxes - those will most likely not be loaded
-		// as we can't make any use out of this data. So consider them
-		// last.
-		if (r == "BoundingBox" || r == "Curve2D") {
-			return 100;
-		}
-		return 0;
-	}
-
-	bool operator() (const IfcRepresentation* a, const IfcRepresentation* b) const {
-		return Rate(a) < Rate(b);
-	}
-};
-
-// ------------------------------------------------------------------------------------------------
-void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< aiNode* >& subnodes, ConversionData& conv)
-{
-	if(!el.Representation) {
-		return;
-	}
-
-
-	std::vector<unsigned int> meshes;
-	
-	// we want only one representation type, so bring them in a suitable order (i.e try those
-	// that look as if we could read them quickly at first). This way of reading
-	// representation is relatively generic and allows the concrete implementations
-	// for the different representation types to make some sensible choices what
-	// to load and what not to load.
-	const STEP::ListOf< STEP::Lazy< IfcRepresentation >, 1, 0 >& src = el.Representation.Get()->Representations;
-
-	std::vector<const IfcRepresentation*> repr_ordered(src.size());
-	std::copy(src.begin(),src.end(),repr_ordered.begin());
-	std::sort(repr_ordered.begin(),repr_ordered.end(),RateRepresentationPredicate());
-
-	BOOST_FOREACH(const IfcRepresentation* repr, repr_ordered) {
-		bool res = false;
-		BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
-			if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
-				res = ProcessMappedItem(*geo,nd,subnodes,conv) || res;
-			}
-			else {
-				res = ProcessRepresentationItem(item,meshes,conv) || res;
-			}
-		}
-		// if we got something meaningful at this point, skip any further representations
-		if(res) {
-			break;
-		}
-	}
-
-	AssignAddedMeshes(meshes,nd,conv);
-}
-
-typedef std::map<std::string, std::string> Metadata;
-
-// ------------------------------------------------------------------------------------------------
-void ProcessMetadata(const ListOf< Lazy< IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties, 
-	const std::string& prefix = "", 
-	unsigned int nest = 0) 
-{
-	BOOST_FOREACH(const IfcProperty& property, set) {
-		const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name;
-		if (const IfcPropertySingleValue* const singleValue = property.ToPtr<IfcPropertySingleValue>()) {
-			if (singleValue->NominalValue) {
-				if (const EXPRESS::STRING* str = singleValue->NominalValue.Get()->ToPtr<EXPRESS::STRING>()) {
-					std::string value = static_cast<std::string>(*str);
-					properties[key]=value;
-				}
-				else if (const EXPRESS::REAL* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::REAL>()) {
-					float value = static_cast<float>(*val);
-					std::stringstream s;
-					s << value;
-					properties[key]=s.str();
-				}
-				else if (const EXPRESS::INTEGER* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::INTEGER>()) {
-					int64_t value = static_cast<int64_t>(*val);
-					std::stringstream s;
-					s << value;
-					properties[key]=s.str();
-				}
-			}
-		}
-		else if (const IfcPropertyListValue* const listValue = property.ToPtr<IfcPropertyListValue>()) {
-			std::stringstream ss;
-			ss << "[";
-			unsigned index=0;
-			BOOST_FOREACH(const IfcValue::Out& v, listValue->ListValues) {
-				if (!v) continue;
-				if (const EXPRESS::STRING* str = v->ToPtr<EXPRESS::STRING>()) {
-					std::string value = static_cast<std::string>(*str);
-					ss << "'" << value << "'";
-				}
-				else if (const EXPRESS::REAL* val = v->ToPtr<EXPRESS::REAL>()) {
-					float value = static_cast<float>(*val);
-					ss << value;
-				}
-				else if (const EXPRESS::INTEGER* val = v->ToPtr<EXPRESS::INTEGER>()) {
-					int64_t value = static_cast<int64_t>(*val);
-					ss << value;
-				}
-				if (index+1<listValue->ListValues.size()) {
-					ss << ",";
-				}
-				index++;
-			}
-			ss << "]";
-			properties[key]=ss.str();
-		}
-		else if (const IfcComplexProperty* const complexProp = property.ToPtr<IfcComplexProperty>()) {
-			if(nest > 2) { // mostly arbitrary limit to prevent stack overflow vulnerabilities
-				IFCImporter::LogError("maximum nesting level for IfcComplexProperty reached, skipping this property.");
-			}
-			else {
-				ProcessMetadata(complexProp->HasProperties, conv, properties, key, nest + 1);
-			}
-		}
-		else {
-			properties[key]="";
-		}
-	}
-}
-
-
-// ------------------------------------------------------------------------------------------------
-void ProcessMetadata(uint64_t relDefinesByPropertiesID, ConversionData& conv, Metadata& properties) 
-{
-	if (const IfcRelDefinesByProperties* const pset = conv.db.GetObject(relDefinesByPropertiesID)->ToPtr<IfcRelDefinesByProperties>()) {
-		if (const IfcPropertySet* const set = conv.db.GetObject(pset->RelatingPropertyDefinition->GetID())->ToPtr<IfcPropertySet>()) {
-			ProcessMetadata(set->HasProperties, conv, properties);			
-		}
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, ConversionData& conv, std::vector<TempOpening>* collect_openings = NULL)
-{
-	const STEP::DB::RefMap& refs = conv.db.GetRefs();
-
-	// skip over space and annotation nodes - usually, these have no meaning in Assimp's context
-	if(conv.settings.skipSpaceRepresentations) {
-		if(const IfcSpace* const space = el.ToPtr<IfcSpace>()) {
-			IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings");
-			return NULL;
-		}
-	}
-
-	if(conv.settings.skipAnnotations) {
-		if(const IfcAnnotation* const ann = el.ToPtr<IfcAnnotation>()) {
-			IFCImporter::LogDebug("skipping IfcAnnotation entity due to importer settings");
-			return NULL;
-		}
-	}
-
-	// add an output node for this spatial structure
-	std::auto_ptr<aiNode> nd(new aiNode());
-	nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name.Get():"Unnamed")+"_"+el.GlobalId);
-	nd->mParent = parent;
-
-	conv.already_processed.insert(el.GetID());
-
-	// check for node metadata
-	STEP::DB::RefMapRange children = refs.equal_range(el.GetID());
-	if (children.first!=refs.end()) {
-		Metadata properties;
-		if (children.first==children.second) {
-			// handles single property set
-			ProcessMetadata((*children.first).second, conv, properties);
-		} 
-		else {
-			// handles multiple property sets (currently all property sets are merged,
-			// which may not be the best solution in the long run)
-			for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) {
-				ProcessMetadata((*it).second, conv, properties);
-			}
-		}
-
-		if (!properties.empty()) {
-			aiMetadata* data = new aiMetadata();
-			data->mNumProperties = properties.size();
-			data->mKeys = new aiString*[data->mNumProperties]();
-			data->mValues = new aiString*[data->mNumProperties]();
-
-			unsigned int i = 0;
-			BOOST_FOREACH(const Metadata::value_type& kv, properties) {
-				data->mKeys[i] = new aiString(kv.first);
-				if (kv.second.length() > 0) {
-					data->mValues[i] = new aiString(kv.second);
-				}				
-				++i;
-			}
-			nd->mMetaData = data;
-		}
-	}
-
-	if(el.ObjectPlacement) {
-		ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv);
-	}
-
-	std::vector<TempOpening> openings;
-
-	IfcMatrix4 myInv;
-	bool didinv = false;
-
-	// convert everything contained directly within this structure,
-	// this may result in more nodes.
-	std::vector< aiNode* > subnodes;
-	try {
-		// locate aggregates and 'contained-in-here'-elements of this spatial structure and add them in recursively
-		// on our way, collect openings in *this* element
-		STEP::DB::RefMapRange range = refs.equal_range(el.GetID());
-
-		for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) {
-			// skip over meshes that have already been processed before. This is strictly necessary
-			// because the reverse indices also include references contained in argument lists and
-			// therefore every element has a back-reference hold by its parent.
-			if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) {
-				continue;
-			}
-			const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second);
-
-			// handle regularly-contained elements
-			if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) {
-				if(cont->RelatingStructure->GetID() != el.GetID()) {
-					continue;
-				}
-				BOOST_FOREACH(const IfcProduct& pro, cont->RelatedElements) {		
-					if(const IfcOpeningElement* const open = pro.ToPtr<IfcOpeningElement>()) {
-						// IfcOpeningElement is handled below. Sadly we can't use it here as is:
-						// The docs say that opening elements are USUALLY attached to building storey,
-						// but we want them for the building elements to which they belong.
-						continue;
-					}
-					
-					aiNode* const ndnew = ProcessSpatialStructure(nd.get(),pro,conv,NULL);
-					if(ndnew) {
-						subnodes.push_back( ndnew );
-					}
-				}
-			}
-			// handle openings, which we collect in a list rather than adding them to the node graph
-			else if(const IfcRelVoidsElement* const fills = obj->ToPtr<IfcRelVoidsElement>()) {
-				if(fills->RelatingBuildingElement->GetID() == el.GetID()) {
-					const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement;
-
-					// move opening elements to a separate node since they are semantically different than elements that are just 'contained'
-					std::auto_ptr<aiNode> nd_aggr(new aiNode());
-					nd_aggr->mName.Set("$RelVoidsElement");
-					nd_aggr->mParent = nd.get();
-
-					nd_aggr->mTransformation = nd->mTransformation;
-
-					std::vector<TempOpening> openings_local;
-					aiNode* const ndnew = ProcessSpatialStructure( nd_aggr.get(),open, conv,&openings_local);
-					if (ndnew) {
-
-						nd_aggr->mNumChildren = 1;
-						nd_aggr->mChildren = new aiNode*[1]();
-
-						
-						nd_aggr->mChildren[0] = ndnew;
-						
-						if(openings_local.size()) {
-							if (!didinv) {
-								myInv = aiMatrix4x4(nd->mTransformation ).Inverse();
-								didinv = true;
-							}
-
-							// we need all openings to be in the local space of *this* node, so transform them
-							BOOST_FOREACH(TempOpening& op,openings_local) {
-								op.Transform( myInv*nd_aggr->mChildren[0]->mTransformation);
-								openings.push_back(op);
-							}
-						}
-						subnodes.push_back( nd_aggr.release() );
-					}
-				}
-			}
-		}
-
-		for(;range.first != range.second; ++range.first) {
-			// see note in loop above
-			if (conv.already_processed.find((*range.first).second) != conv.already_processed.end()) {
-				continue;
-			}
-			if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
-				if(aggr->RelatingObject->GetID() != el.GetID()) {
-					continue;
-				}
-
-				// move aggregate elements to a separate node since they are semantically different than elements that are just 'contained'
-				std::auto_ptr<aiNode> nd_aggr(new aiNode());
-				nd_aggr->mName.Set("$RelAggregates");
-				nd_aggr->mParent = nd.get();
-
-				nd_aggr->mTransformation = nd->mTransformation;
-
-				nd_aggr->mChildren = new aiNode*[aggr->RelatedObjects.size()]();
-				BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
-					if(const IfcProduct* const prod = def.ToPtr<IfcProduct>()) {
-
-						aiNode* const ndnew = ProcessSpatialStructure(nd_aggr.get(),*prod,conv,NULL);
-						if(ndnew) {
-							nd_aggr->mChildren[nd_aggr->mNumChildren++] = ndnew;
-						}
-					}
-				}
-			
-				subnodes.push_back( nd_aggr.release() );
-			}
-		}
-
-		conv.collect_openings = collect_openings;
-		if(!conv.collect_openings) {
-			conv.apply_openings = &openings;
-		}
-
-		ProcessProductRepresentation(el,nd.get(),subnodes,conv);
-		conv.apply_openings = conv.collect_openings = NULL;
-
-		if (subnodes.size()) {
-			nd->mChildren = new aiNode*[subnodes.size()]();
-			BOOST_FOREACH(aiNode* nd2, subnodes) {
-				nd->mChildren[nd->mNumChildren++] = nd2;
-				nd2->mParent = nd.get();
-			}
-		}
-	}
-	catch(...) {
-		// it hurts, but I don't want to pull boost::ptr_vector into -noboost only for these few spots here
-		std::for_each(subnodes.begin(),subnodes.end(),delete_fun<aiNode>());
-		throw;
-	}
-
-	ai_assert(conv.already_processed.find(el.GetID()) != conv.already_processed.end());
-	conv.already_processed.erase(conv.already_processed.find(el.GetID()));
-	return nd.release();
-}
-
-// ------------------------------------------------------------------------------------------------
-void ProcessSpatialStructures(ConversionData& conv)
-{
-	// XXX add support for multiple sites (i.e. IfcSpatialStructureElements with composition == COMPLEX)
-
-
-	// process all products in the file. it is reasonable to assume that a
-	// file that is relevant for us contains at least a site or a building.
-	const STEP::DB::ObjectMapByType& map = conv.db.GetObjectsByType();
-
-	ai_assert(map.find("ifcsite") != map.end());
-	const STEP::DB::ObjectSet* range = &map.find("ifcsite")->second;
-
-	if (range->empty()) {
-		ai_assert(map.find("ifcbuilding") != map.end());
-		range = &map.find("ifcbuilding")->second;
-		if (range->empty()) {
-			// no site, no building -  fail;
-			IFCImporter::ThrowException("no root element found (expected IfcBuilding or preferably IfcSite)");
-		}
-	}
-
-	
-	BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
-		const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
-		if(!prod) {
-			continue;
-		}
-		IFCImporter::LogDebug("looking at spatial structure `" + (prod->Name ? prod->Name.Get() : "unnamed") + "`" + (prod->ObjectType? " which is of type " + prod->ObjectType.Get():""));
-	
-		// the primary site is referenced by an IFCRELAGGREGATES element which assigns it to the IFCPRODUCT
-		const STEP::DB::RefMap& refs = conv.db.GetRefs();
-		STEP::DB::RefMapRange range = refs.equal_range(conv.proj.GetID());
-		for(;range.first != range.second; ++range.first) {
-			if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
-			
-				BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
-					// comparing pointer values is not sufficient, we would need to cast them to the same type first
-					// as there is multiple inheritance in the game.
-					if (def.GetID() == prod->GetID()) { 
-						IFCImporter::LogDebug("selecting this spatial structure as root structure");
-						// got it, this is the primary site.
-						conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
-						return;
-					}
-				}
-
-			}
-		}
-	}
-
-	
-	IFCImporter::LogWarn("failed to determine primary site element, taking the first IfcSite");
-	BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
-		const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
-		if(!prod) {
-			continue;
-		}
-
-		conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
-		return;
-	}
-
-	IFCImporter::ThrowException("failed to determine primary site element");
-}
-
-// ------------------------------------------------------------------------------------------------
-void MakeTreeRelative(aiNode* start, const aiMatrix4x4& combined)
-{
-	// combined is the parent's absolute transformation matrix
-	const aiMatrix4x4 old = start->mTransformation;
-
-	if (!combined.IsIdentity()) {
-		start->mTransformation = aiMatrix4x4(combined).Inverse() * start->mTransformation;
-	}
-
-	// All nodes store absolute transformations right now, so we need to make them relative
-	for (unsigned int i = 0; i < start->mNumChildren; ++i) {
-		MakeTreeRelative(start->mChildren[i],old);
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void MakeTreeRelative(ConversionData& conv)
-{
-	MakeTreeRelative(conv.out->mRootNode,IfcMatrix4());
-}
-
-} // !anon
-
-
-
-#endif
+		}
+		else {
+			ThrowException("Found no IFC file member in IFCZIP file (2)");
+		}
+
+		unzClose(zip);
+#else
+		ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support");
+#endif
+	}
+
+	boost::scoped_ptr<STEP::DB> db(STEP::ReadFileHeader(stream));
+	const STEP::HeaderInfo& head = static_cast<const STEP::DB&>(*db).GetHeader();
+
+	if(!head.fileSchema.size() || head.fileSchema.substr(0,3) != "IFC") {
+		ThrowException("Unrecognized file schema: " + head.fileSchema);
+	}
+
+	if (!DefaultLogger::isNullLogger()) {
+		LogDebug("File schema is \'" + head.fileSchema + '\'');
+		if (head.timestamp.length()) {
+			LogDebug("Timestamp \'" + head.timestamp + '\'');
+		}
+		if (head.app.length()) {
+			LogDebug("Application/Exporter identline is \'" + head.app  + '\'');
+		}
+	}
+
+	// obtain a copy of the machine-generated IFC scheme
+	EXPRESS::ConversionSchema schema;
+	GetSchema(schema);
+
+	// tell the reader which entity types to track with special care
+	static const char* const types_to_track[] = {
+		"ifcsite", "ifcbuilding", "ifcproject"
+	};
+
+	// tell the reader for which types we need to simulate STEPs reverse indices
+	static const char* const inverse_indices_to_track[] = {
+		"ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
+	};
+
+	// feed the IFC schema into the reader and pre-parse all lines
+	STEP::ReadFile(*db, schema, types_to_track, inverse_indices_to_track);
+	const STEP::LazyObject* proj =  db->GetObject("ifcproject");
+	if (!proj) {
+		ThrowException("missing IfcProject entity");
+	}
+
+	ConversionData conv(*db,proj->To<IfcProject>(),pScene,settings);
+	SetUnits(conv);
+	SetCoordinateSpace(conv);
+	ProcessSpatialStructures(conv);
+	MakeTreeRelative(conv);
+
+	// NOTE - this is a stress test for the importer, but it works only
+	// in a build with no entities disabled. See 
+	//     scripts/IFCImporter/CPPGenerator.py
+	// for more information.
+	#ifdef ASSIMP_IFC_TEST
+		db->EvaluateAll();
+	#endif
+
+	// do final data copying
+	if (conv.meshes.size()) {
+		pScene->mNumMeshes = static_cast<unsigned int>(conv.meshes.size());
+		pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
+		std::copy(conv.meshes.begin(),conv.meshes.end(),pScene->mMeshes);
+
+		// needed to keep the d'tor from burning us
+		conv.meshes.clear();
+	}
+
+	if (conv.materials.size()) {
+		pScene->mNumMaterials = static_cast<unsigned int>(conv.materials.size());
+		pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
+		std::copy(conv.materials.begin(),conv.materials.end(),pScene->mMaterials);
+
+		// needed to keep the d'tor from burning us
+		conv.materials.clear();
+	}
+
+	// apply world coordinate system (which includes the scaling to convert to meters and a -90 degrees rotation around x)
+	aiMatrix4x4 scale, rot;
+	aiMatrix4x4::Scaling(static_cast<aiVector3D>(IfcVector3(conv.len_scale)),scale);
+	aiMatrix4x4::RotationX(-AI_MATH_HALF_PI_F,rot);
+
+	pScene->mRootNode->mTransformation = rot * scale * conv.wcs * pScene->mRootNode->mTransformation;
+
+	// this must be last because objects are evaluated lazily as we process them
+	if ( !DefaultLogger::isNullLogger() ){
+		LogDebug((Formatter::format(),"STEP: evaluated ",db->GetEvaluatedObjectCount()," object records"));
+	}
+}
+
+namespace {
+
+
+// ------------------------------------------------------------------------------------------------
+void ConvertUnit(const IfcNamedUnit& unit,ConversionData& conv)
+{
+	if(const IfcSIUnit* const si = unit.ToPtr<IfcSIUnit>()) {
+
+		if(si->UnitType == "LENGTHUNIT") { 
+			conv.len_scale = si->Prefix ? ConvertSIPrefix(si->Prefix) : 1.f;
+			IFCImporter::LogDebug("got units used for lengths");
+		}
+		if(si->UnitType == "PLANEANGLEUNIT") { 
+			if (si->Name != "RADIAN") {
+				IFCImporter::LogWarn("expected base unit for angles to be radian");
+			}
+		}
+	}
+	else if(const IfcConversionBasedUnit* const convu = unit.ToPtr<IfcConversionBasedUnit>()) {
+
+		if(convu->UnitType == "PLANEANGLEUNIT") { 
+			try {
+				conv.angle_scale = convu->ConversionFactor->ValueComponent->To<EXPRESS::REAL>();
+				ConvertUnit(*convu->ConversionFactor->UnitComponent,conv);
+				IFCImporter::LogDebug("got units used for angles");
+			}
+			catch(std::bad_cast&) {
+				IFCImporter::LogError("skipping unknown IfcConversionBasedUnit.ValueComponent entry - expected REAL");
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv)
+{
+	try {
+		const EXPRESS::ENTITY& e = dt.To<ENTITY>();
+
+		const IfcNamedUnit& unit = e.ResolveSelect<IfcNamedUnit>(conv.db);
+		if(unit.UnitType != "LENGTHUNIT" && unit.UnitType != "PLANEANGLEUNIT") {
+			return;
+		}
+
+		ConvertUnit(unit,conv);
+	}
+	catch(std::bad_cast&) {
+		// not entity, somehow
+		IFCImporter::LogError("skipping unknown IfcUnit entry - expected entity");
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void SetUnits(ConversionData& conv)
+{
+	// see if we can determine the coordinate space used to express. 
+	for(size_t i = 0; i <  conv.proj.UnitsInContext->Units.size(); ++i ) {
+		ConvertUnit(*conv.proj.UnitsInContext->Units[i],conv);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void SetCoordinateSpace(ConversionData& conv)
+{
+	const IfcRepresentationContext* fav = NULL;
+	BOOST_FOREACH(const IfcRepresentationContext& v, conv.proj.RepresentationContexts) {
+		fav = &v;
+		// Model should be the most suitable type of context, hence ignore the others 
+		if (v.ContextType && v.ContextType.Get() == "Model") { 
+			break;
+		}
+	}
+	if (fav) {
+		if(const IfcGeometricRepresentationContext* const geo = fav->ToPtr<IfcGeometricRepresentationContext>()) {
+			ConvertAxisPlacement(conv.wcs, *geo->WorldCoordinateSystem, conv);
+			IFCImporter::LogDebug("got world coordinate system");
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void ResolveObjectPlacement(aiMatrix4x4& m, const IfcObjectPlacement& place, ConversionData& conv)
+{
+	if (const IfcLocalPlacement* const local = place.ToPtr<IfcLocalPlacement>()){
+		IfcMatrix4 tmp;
+		ConvertAxisPlacement(tmp, *local->RelativePlacement, conv);
+
+		m = static_cast<aiMatrix4x4>(tmp);
+
+		if (local->PlacementRelTo) {
+			aiMatrix4x4 tmp;
+			ResolveObjectPlacement(tmp,local->PlacementRelTo.Get(),conv);
+			m = tmp * m;
+		}
+	}
+	else {
+		IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is " + place.GetClassName());
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
+{
+	aiMatrix4x4 t;
+	if (nd->mParent) {
+		GetAbsTransform(t,nd->mParent,conv);
+	}
+	out = t*nd->mTransformation;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
+{
+	// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
+	std::auto_ptr<aiNode> nd(new aiNode());
+	nd->mName.Set("IfcMappedItem");
+		
+	// handle the Cartesian operator
+	IfcMatrix4 m;
+	ConvertTransformOperator(m, *mapped.MappingTarget);
+
+	IfcMatrix4 msrc;
+	ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
+
+	msrc = m*msrc;
+
+	std::vector<unsigned int> meshes;
+	const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0;
+	if (conv.apply_openings) {
+		IfcMatrix4 minv = msrc;
+		minv.Inverse();
+		BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
+			open.Transform(minv);
+		}
+	}
+
+	const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
+
+	bool got = false;
+	BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
+		if(!ProcessRepresentationItem(item,meshes,conv)) {
+			IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
+		}
+		else got = true;
+	}
+
+	if (!got) {
+		return false;
+	}
+
+	AssignAddedMeshes(meshes,nd.get(),conv);
+	if (conv.collect_openings) {
+
+		// if this pass serves us only to collect opening geometry,
+		// make sure we transform the TempMesh's which we need to
+		// preserve as well.
+		if(const size_t diff = conv.collect_openings->size() - old_openings) {
+			for(size_t i = 0; i < diff; ++i) {
+				(*conv.collect_openings)[old_openings+i].Transform(msrc);
+			}
+		}
+	}
+
+	nd->mTransformation =  nd_src->mTransformation * static_cast<aiMatrix4x4>( msrc );
+	subnodes_src.push_back(nd.release());
+
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+struct RateRepresentationPredicate {
+
+	int Rate(const IfcRepresentation* r) const {
+		// the smaller, the better
+
+		if (! r->RepresentationIdentifier) {
+			// neutral choice if no extra information is specified
+			return 0;
+		}
+
+		
+		const std::string& name = r->RepresentationIdentifier.Get();
+		if (name == "MappedRepresentation") {
+			if (!r->Items.empty()) {
+				// take the first item and base our choice on it
+				const IfcMappedItem* const m = r->Items.front()->ToPtr<IfcMappedItem>();
+				if (m) {
+					return Rate(m->MappingSource->MappedRepresentation);
+				}
+			}
+			return 100;
+		}
+
+		return Rate(name);
+	}
+
+	int Rate(const std::string& r) const {
+
+
+		if (r == "SolidModel") {
+			return -3;
+		}
+		
+		// give strong preference to extruded geometry.
+		if (r == "SweptSolid") {
+			return -10;
+		}
+		
+		if (r == "Clipping") {
+			return -5;
+		}
+
+		// 'Brep' is difficult to get right due to possible voids in the
+		// polygon boundaries, so take it only if we are forced to (i.e.
+		// if the only alternative is (non-clipping) boolean operations, 
+		// which are not supported at all).
+		if (r == "Brep") {
+			return -2;
+		}
+		
+		// Curves, bounding boxes - those will most likely not be loaded
+		// as we can't make any use out of this data. So consider them
+		// last.
+		if (r == "BoundingBox" || r == "Curve2D") {
+			return 100;
+		}
+		return 0;
+	}
+
+	bool operator() (const IfcRepresentation* a, const IfcRepresentation* b) const {
+		return Rate(a) < Rate(b);
+	}
+};
+
+// ------------------------------------------------------------------------------------------------
+void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< aiNode* >& subnodes, ConversionData& conv)
+{
+	if(!el.Representation) {
+		return;
+	}
+	std::vector<unsigned int> meshes;
+	// we want only one representation type, so bring them in a suitable order (i.e try those
+	// that look as if we could read them quickly at first). This way of reading
+	// representation is relatively generic and allows the concrete implementations
+	// for the different representation types to make some sensible choices what
+	// to load and what not to load.
+	const STEP::ListOf< STEP::Lazy< IfcRepresentation >, 1, 0 >& src = el.Representation.Get()->Representations;
+	std::vector<const IfcRepresentation*> repr_ordered(src.size());
+	std::copy(src.begin(),src.end(),repr_ordered.begin());
+	std::sort(repr_ordered.begin(),repr_ordered.end(),RateRepresentationPredicate());
+	BOOST_FOREACH(const IfcRepresentation* repr, repr_ordered) {
+		bool res = false;
+		BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
+			if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
+				res = ProcessMappedItem(*geo,nd,subnodes,conv) || res;
+			}
+			else {
+				res = ProcessRepresentationItem(item,meshes,conv) || res;
+			}
+		}
+		// if we got something meaningful at this point, skip any further representations
+		if(res) {
+			break;
+		}
+	}
+	AssignAddedMeshes(meshes,nd,conv);
+}
+
+typedef std::map<std::string, std::string> Metadata;
+
+// ------------------------------------------------------------------------------------------------
+void ProcessMetadata(const ListOf< Lazy< IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties, 
+	const std::string& prefix = "", 
+	unsigned int nest = 0) 
+{
+	BOOST_FOREACH(const IfcProperty& property, set) {
+		const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name;
+		if (const IfcPropertySingleValue* const singleValue = property.ToPtr<IfcPropertySingleValue>()) {
+			if (singleValue->NominalValue) {
+				if (const EXPRESS::STRING* str = singleValue->NominalValue.Get()->ToPtr<EXPRESS::STRING>()) {
+					std::string value = static_cast<std::string>(*str);
+					properties[key]=value;
+				}
+				else if (const EXPRESS::REAL* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::REAL>()) {
+					float value = static_cast<float>(*val);
+					std::stringstream s;
+					s << value;
+					properties[key]=s.str();
+				}
+				else if (const EXPRESS::INTEGER* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::INTEGER>()) {
+					int64_t value = static_cast<int64_t>(*val);
+					std::stringstream s;
+					s << value;
+					properties[key]=s.str();
+				}
+			}
+		}
+		else if (const IfcPropertyListValue* const listValue = property.ToPtr<IfcPropertyListValue>()) {
+			std::stringstream ss;
+			ss << "[";
+			unsigned index=0;
+			BOOST_FOREACH(const IfcValue::Out& v, listValue->ListValues) {
+				if (!v) continue;
+				if (const EXPRESS::STRING* str = v->ToPtr<EXPRESS::STRING>()) {
+					std::string value = static_cast<std::string>(*str);
+					ss << "'" << value << "'";
+				}
+				else if (const EXPRESS::REAL* val = v->ToPtr<EXPRESS::REAL>()) {
+					float value = static_cast<float>(*val);
+					ss << value;
+				}
+				else if (const EXPRESS::INTEGER* val = v->ToPtr<EXPRESS::INTEGER>()) {
+					int64_t value = static_cast<int64_t>(*val);
+					ss << value;
+				}
+				if (index+1<listValue->ListValues.size()) {
+					ss << ",";
+				}
+				index++;
+			}
+			ss << "]";
+			properties[key]=ss.str();
+		}
+		else if (const IfcComplexProperty* const complexProp = property.ToPtr<IfcComplexProperty>()) {
+			if(nest > 2) { // mostly arbitrary limit to prevent stack overflow vulnerabilities
+				IFCImporter::LogError("maximum nesting level for IfcComplexProperty reached, skipping this property.");
+			}
+			else {
+				ProcessMetadata(complexProp->HasProperties, conv, properties, key, nest + 1);
+			}
+		}
+		else {
+			properties[key]="";
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void ProcessMetadata(uint64_t relDefinesByPropertiesID, ConversionData& conv, Metadata& properties) 
+{
+	if (const IfcRelDefinesByProperties* const pset = conv.db.GetObject(relDefinesByPropertiesID)->ToPtr<IfcRelDefinesByProperties>()) {
+		if (const IfcPropertySet* const set = conv.db.GetObject(pset->RelatingPropertyDefinition->GetID())->ToPtr<IfcPropertySet>()) {
+			ProcessMetadata(set->HasProperties, conv, properties);			
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, ConversionData& conv, std::vector<TempOpening>* collect_openings = NULL)
+{
+	const STEP::DB::RefMap& refs = conv.db.GetRefs();
+
+	// skip over space and annotation nodes - usually, these have no meaning in Assimp's context
+	if(conv.settings.skipSpaceRepresentations) {
+		if(const IfcSpace* const space = el.ToPtr<IfcSpace>()) {
+			IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings");
+			return NULL;
+		}
+	}
+
+	if(conv.settings.skipAnnotations) {
+		if(const IfcAnnotation* const ann = el.ToPtr<IfcAnnotation>()) {
+			IFCImporter::LogDebug("skipping IfcAnnotation entity due to importer settings");
+			return NULL;
+		}
+	}
+
+	// add an output node for this spatial structure
+	std::auto_ptr<aiNode> nd(new aiNode());
+	nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name.Get():"Unnamed")+"_"+el.GlobalId);
+	nd->mParent = parent;
+
+	conv.already_processed.insert(el.GetID());
+
+	// check for node metadata
+	STEP::DB::RefMapRange children = refs.equal_range(el.GetID());
+	if (children.first!=refs.end()) {
+		Metadata properties;
+		if (children.first==children.second) {
+			// handles single property set
+			ProcessMetadata((*children.first).second, conv, properties);
+		} 
+		else {
+			// handles multiple property sets (currently all property sets are merged,
+			// which may not be the best solution in the long run)
+			for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) {
+				ProcessMetadata((*it).second, conv, properties);
+			}
+		}
+
+		if (!properties.empty()) {
+			aiMetadata* data = new aiMetadata();
+			data->mNumProperties = properties.size();
+			data->mKeys = new aiString*[data->mNumProperties]();
+			data->mValues = new aiString*[data->mNumProperties]();
+
+			unsigned int i = 0;
+			BOOST_FOREACH(const Metadata::value_type& kv, properties) {
+				data->mKeys[i] = new aiString(kv.first);
+				if (kv.second.length() > 0) {
+					data->mValues[i] = new aiString(kv.second);
+				}				
+				++i;
+			}
+			nd->mMetaData = data;
+		}
+	}
+
+	if(el.ObjectPlacement) {
+		ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv);
+	}
+
+	std::vector<TempOpening> openings;
+
+	IfcMatrix4 myInv;
+	bool didinv = false;
+
+	// convert everything contained directly within this structure,
+	// this may result in more nodes.
+	std::vector< aiNode* > subnodes;
+	try {
+		// locate aggregates and 'contained-in-here'-elements of this spatial structure and add them in recursively
+		// on our way, collect openings in *this* element
+		STEP::DB::RefMapRange range = refs.equal_range(el.GetID());
+
+		for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) {
+			// skip over meshes that have already been processed before. This is strictly necessary
+			// because the reverse indices also include references contained in argument lists and
+			// therefore every element has a back-reference hold by its parent.
+			if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) {
+				continue;
+			}
+			const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second);
+
+			// handle regularly-contained elements
+			if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) {
+				if(cont->RelatingStructure->GetID() != el.GetID()) {
+					continue;
+				}
+				BOOST_FOREACH(const IfcProduct& pro, cont->RelatedElements) {		
+					if(const IfcOpeningElement* const open = pro.ToPtr<IfcOpeningElement>()) {
+						// IfcOpeningElement is handled below. Sadly we can't use it here as is:
+						// The docs say that opening elements are USUALLY attached to building storey,
+						// but we want them for the building elements to which they belong.
+						continue;
+					}
+					
+					aiNode* const ndnew = ProcessSpatialStructure(nd.get(),pro,conv,NULL);
+					if(ndnew) {
+						subnodes.push_back( ndnew );
+					}
+				}
+			}
+			// handle openings, which we collect in a list rather than adding them to the node graph
+			else if(const IfcRelVoidsElement* const fills = obj->ToPtr<IfcRelVoidsElement>()) {
+				if(fills->RelatingBuildingElement->GetID() == el.GetID()) {
+					const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement;
+
+					// move opening elements to a separate node since they are semantically different than elements that are just 'contained'
+					std::auto_ptr<aiNode> nd_aggr(new aiNode());
+					nd_aggr->mName.Set("$RelVoidsElement");
+					nd_aggr->mParent = nd.get();
+
+					nd_aggr->mTransformation = nd->mTransformation;
+
+					std::vector<TempOpening> openings_local;
+					aiNode* const ndnew = ProcessSpatialStructure( nd_aggr.get(),open, conv,&openings_local);
+					if (ndnew) {
+
+						nd_aggr->mNumChildren = 1;
+						nd_aggr->mChildren = new aiNode*[1]();
+
+						
+						nd_aggr->mChildren[0] = ndnew;
+						
+						if(openings_local.size()) {
+							if (!didinv) {
+								myInv = aiMatrix4x4(nd->mTransformation ).Inverse();
+								didinv = true;
+							}
+
+							// we need all openings to be in the local space of *this* node, so transform them
+							BOOST_FOREACH(TempOpening& op,openings_local) {
+								op.Transform( myInv*nd_aggr->mChildren[0]->mTransformation);
+								openings.push_back(op);
+							}
+						}
+						subnodes.push_back( nd_aggr.release() );
+					}
+				}
+			}
+		}
+
+		for(;range.first != range.second; ++range.first) {
+			// see note in loop above
+			if (conv.already_processed.find((*range.first).second) != conv.already_processed.end()) {
+				continue;
+			}
+			if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
+				if(aggr->RelatingObject->GetID() != el.GetID()) {
+					continue;
+				}
+
+				// move aggregate elements to a separate node since they are semantically different than elements that are just 'contained'
+				std::auto_ptr<aiNode> nd_aggr(new aiNode());
+				nd_aggr->mName.Set("$RelAggregates");
+				nd_aggr->mParent = nd.get();
+
+				nd_aggr->mTransformation = nd->mTransformation;
+
+				nd_aggr->mChildren = new aiNode*[aggr->RelatedObjects.size()]();
+				BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
+					if(const IfcProduct* const prod = def.ToPtr<IfcProduct>()) {
+
+						aiNode* const ndnew = ProcessSpatialStructure(nd_aggr.get(),*prod,conv,NULL);
+						if(ndnew) {
+							nd_aggr->mChildren[nd_aggr->mNumChildren++] = ndnew;
+						}
+					}
+				}
+			
+				subnodes.push_back( nd_aggr.release() );
+			}
+		}
+
+		conv.collect_openings = collect_openings;
+		if(!conv.collect_openings) {
+			conv.apply_openings = &openings;
+		}
+
+		ProcessProductRepresentation(el,nd.get(),subnodes,conv);
+		conv.apply_openings = conv.collect_openings = NULL;
+
+		if (subnodes.size()) {
+			nd->mChildren = new aiNode*[subnodes.size()]();
+			BOOST_FOREACH(aiNode* nd2, subnodes) {
+				nd->mChildren[nd->mNumChildren++] = nd2;
+				nd2->mParent = nd.get();
+			}
+		}
+	}
+	catch(...) {
+		// it hurts, but I don't want to pull boost::ptr_vector into -noboost only for these few spots here
+		std::for_each(subnodes.begin(),subnodes.end(),delete_fun<aiNode>());
+		throw;
+	}
+
+	ai_assert(conv.already_processed.find(el.GetID()) != conv.already_processed.end());
+	conv.already_processed.erase(conv.already_processed.find(el.GetID()));
+	return nd.release();
+}
+
+// ------------------------------------------------------------------------------------------------
+void ProcessSpatialStructures(ConversionData& conv)
+{
+	// XXX add support for multiple sites (i.e. IfcSpatialStructureElements with composition == COMPLEX)
+
+
+	// process all products in the file. it is reasonable to assume that a
+	// file that is relevant for us contains at least a site or a building.
+	const STEP::DB::ObjectMapByType& map = conv.db.GetObjectsByType();
+
+	ai_assert(map.find("ifcsite") != map.end());
+	const STEP::DB::ObjectSet* range = &map.find("ifcsite")->second;
+
+	if (range->empty()) {
+		ai_assert(map.find("ifcbuilding") != map.end());
+		range = &map.find("ifcbuilding")->second;
+		if (range->empty()) {
+			// no site, no building -  fail;
+			IFCImporter::ThrowException("no root element found (expected IfcBuilding or preferably IfcSite)");
+		}
+	}
+
+	
+	BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
+		const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
+		if(!prod) {
+			continue;
+		}
+		IFCImporter::LogDebug("looking at spatial structure `" + (prod->Name ? prod->Name.Get() : "unnamed") + "`" + (prod->ObjectType? " which is of type " + prod->ObjectType.Get():""));
+	
+		// the primary site is referenced by an IFCRELAGGREGATES element which assigns it to the IFCPRODUCT
+		const STEP::DB::RefMap& refs = conv.db.GetRefs();
+		STEP::DB::RefMapRange range = refs.equal_range(conv.proj.GetID());
+		for(;range.first != range.second; ++range.first) {
+			if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
+			
+				BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
+					// comparing pointer values is not sufficient, we would need to cast them to the same type first
+					// as there is multiple inheritance in the game.
+					if (def.GetID() == prod->GetID()) { 
+						IFCImporter::LogDebug("selecting this spatial structure as root structure");
+						// got it, this is the primary site.
+						conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
+						return;
+					}
+				}
+
+			}
+		}
+	}
+
+	
+	IFCImporter::LogWarn("failed to determine primary site element, taking the first IfcSite");
+	BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
+		const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
+		if(!prod) {
+			continue;
+		}
+
+		conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
+		return;
+	}
+
+	IFCImporter::ThrowException("failed to determine primary site element");
+}
+
+// ------------------------------------------------------------------------------------------------
+void MakeTreeRelative(aiNode* start, const aiMatrix4x4& combined)
+{
+	// combined is the parent's absolute transformation matrix
+	const aiMatrix4x4 old = start->mTransformation;
+
+	if (!combined.IsIdentity()) {
+		start->mTransformation = aiMatrix4x4(combined).Inverse() * start->mTransformation;
+	}
+
+	// All nodes store absolute transformations right now, so we need to make them relative
+	for (unsigned int i = 0; i < start->mNumChildren; ++i) {
+		MakeTreeRelative(start->mChildren[i],old);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void MakeTreeRelative(ConversionData& conv)
+{
+	MakeTreeRelative(conv.out->mRootNode,IfcMatrix4());
+}
+
+} // !anon
+
+
+
+#endif

+ 238 - 242
code/LineSplitter.h

@@ -1,242 +1,238 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2012, assimp team
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms, 
-with or without modification, are permitted provided that the 
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-
-/** @file  LineSplitter.h
- *  @brief LineSplitter, a helper class to iterate through all lines
- *    of a file easily. Works with StreamReader.
- */
-#ifndef INCLUDED_LINE_SPLITTER_H
-#define INCLUDED_LINE_SPLITTER_H
-
-#include <stdexcept>
-
-#include "StreamReader.h"
-#include "ParsingUtils.h"
-
-namespace Assimp {
-
-// ------------------------------------------------------------------------------------------------
-/** Usage:
-@code
-for(LineSplitter splitter(stream);splitter;++splitter) {
-
-	if (*splitter == "hi!") {
-	   ...
-	}
-    else if (splitter->substr(0,5) == "hello") {
-	   ...
-	   // access the third token in the line (tokens are space-separated)
-	   if (strtol(splitter[2]) > 5) { .. }
-	}
-
-	std::cout << "Current line is: " << splitter.get_index() << std::endl;
-}
-@endcode */
-// ------------------------------------------------------------------------------------------------
-class LineSplitter
-{
-public:
-
-	typedef size_t line_idx;
-
-public:
-
-	// -----------------------------------------
-	/** construct from existing stream reader 
-	note: trim is *always* assumed true if skyp_empty_lines==true
-	*/
-	LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true)
-		: stream(stream)
-		, swallow()
-		, skip_empty_lines(skip_empty_lines)
-		, trim(trim)
-	{
-		cur.reserve(1024);
-		operator++();
-
-		idx = 0;
-	}
-
-public:
-
-	// -----------------------------------------
-	/** pseudo-iterator increment */
-	LineSplitter& operator++() {
-		if(swallow) {
-			swallow = false;
-			return *this;
-		}
-		
-		if (!*this) {
-			throw std::logic_error("End of file, no more lines to be retrieved.");
-		}
-
-		char s;
-
-		cur.clear();
-		while(stream.GetRemainingSize() && (s = stream.GetI1(),1)) {
-			if (s == '\n' || s == '\r') {
-				if (skip_empty_lines) {
-					while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\r' || s == '\n'));
-					if (stream.GetRemainingSize()) {
-						stream.IncPtr(-1);
-					}
-				}
-				else {
-					// skip both potential line terminators but don't read past this line.
-					if (stream.GetRemainingSize() && (s == '\r' && stream.GetI1() != '\n')) {
-						stream.IncPtr(-1);
-					}
-
-					if (trim) {
-						while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\t'));
-						if (stream.GetRemainingSize()) {
-							stream.IncPtr(-1);
-						}
-					}
-				}
-				
-				break;
-			}
-			cur += s;
-		}
-
-		++idx;
-		return *this;
-	}
-
-	// -----------------------------------------
-	LineSplitter& operator++(int) {
-		return ++(*this);
-	}
-
-	// -----------------------------------------
-	/** get a pointer to the beginning of a particular token */
-	const char* operator[] (size_t idx) const {
-		const char* s = operator->()->c_str();
-
-		SkipSpaces(&s);
-		for(size_t i = 0; i < idx; ++i) {
-
-			for(;!IsSpace(*s); ++s) {
-				if(IsLineEnd(*s)) {
-					throw std::range_error("Token index out of range, EOL reached");
-				}
-			}
-			SkipSpaces(&s);
-		}
-		return s;
-	}
-
-	// -----------------------------------------
-	/** extract the start positions of N tokens from the current line*/
-	template <size_t N>
-	void get_tokens(const char* (&tokens)[N]) const {
-		const char* s = operator->()->c_str();
-
-		SkipSpaces(&s);
-		for(size_t i = 0; i < N; ++i) {
-			if(IsLineEnd(*s)) {
-				throw std::range_error("Token count out of range, EOL reached");
-			}
-			tokens[i] = s;
-
-			for(;*s && !IsSpace(*s); ++s);
-			SkipSpaces(&s);
-		}
-	}
-
-	// -----------------------------------------
-	/** member access */
-	const std::string* operator -> () const {
-		return &cur;
-	}
-
-	std::string operator* () const {
-		return cur;
-	}
-
-	// -----------------------------------------
-	/** boolean context */
-	operator bool() const {
-		return stream.GetRemainingSize()>0;
-	}
-
-	// -----------------------------------------
-	/** line indices are zero-based, empty lines are included */
-	operator line_idx() const {
-		return idx;
-	}
-
-	line_idx get_index() const {
-		return idx;
-	}
-
-	// -----------------------------------------
-	/** access the underlying stream object */
-	StreamReaderLE& get_stream() {
-		return stream;
-	}
-
-	// -----------------------------------------
-	/** !strcmp((*this)->substr(0,strlen(check)),check) */
-	bool match_start(const char* check) {
-		const size_t len = strlen(check);
-		
-		return len <= cur.length() && std::equal(check,check+len,cur.begin());
-	}
-
-
-	// -----------------------------------------
-	/** swallow the next call to ++, return the previous value. */
-	void swallow_next_increment() {
-		swallow = true;
-	}
-
-private:
-
-	line_idx idx;
-	std::string cur;
-	StreamReaderLE& stream;
-	bool swallow, skip_empty_lines, trim;
-};
-
-}
-#endif // INCLUDED_LINE_SPLITTER_H
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  LineSplitter.h
+ *  @brief LineSplitter, a helper class to iterate through all lines
+ *    of a file easily. Works with StreamReader.
+ */
+#ifndef INCLUDED_LINE_SPLITTER_H
+#define INCLUDED_LINE_SPLITTER_H
+
+#include <stdexcept>
+
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/** Usage:
+@code
+for(LineSplitter splitter(stream);splitter;++splitter) {
+
+	if (*splitter == "hi!") {
+	   ...
+	}
+    else if (splitter->substr(0,5) == "hello") {
+	   ...
+	   // access the third token in the line (tokens are space-separated)
+	   if (strtol(splitter[2]) > 5) { .. }
+	}
+
+	std::cout << "Current line is: " << splitter.get_index() << std::endl;
+}
+@endcode */
+// ------------------------------------------------------------------------------------------------
+class LineSplitter
+{
+public:
+
+	typedef size_t line_idx;
+
+public:
+
+	// -----------------------------------------
+	/** construct from existing stream reader 
+	note: trim is *always* assumed true if skyp_empty_lines==true
+	*/
+	LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true)
+		: stream(stream)
+		, swallow()
+		, skip_empty_lines(skip_empty_lines)
+		, trim(trim)
+	{
+		cur.reserve(1024);
+		operator++();
+
+		idx = 0;
+	}
+
+public:
+
+	// -----------------------------------------
+	/** pseudo-iterator increment */
+	LineSplitter& operator++() {
+		if(swallow) {
+			swallow = false;
+			return *this;
+		}
+		if (!*this) {
+			throw std::logic_error("End of file, no more lines to be retrieved.");
+		}
+		char s;
+		cur.clear();
+		while(stream.GetRemainingSize() && (s = stream.GetI1(),1)) {
+			if (s == '\n' || s == '\r') {
+				if (skip_empty_lines) {
+					while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\r' || s == '\n'));
+					if (stream.GetRemainingSize()) {
+						stream.IncPtr(-1);
+					}
+				}
+				else {
+					// skip both potential line terminators but don't read past this line.
+					if (stream.GetRemainingSize() && (s == '\r' && stream.GetI1() != '\n')) {
+						stream.IncPtr(-1);
+					}
+					if (trim) {
+						while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\t'));
+						if (stream.GetRemainingSize()) {
+							stream.IncPtr(-1);
+						}
+					}
+				}
+				break;
+			}
+			cur += s;
+		}
+		++idx;
+		return *this;
+	}
+
+	// -----------------------------------------
+	LineSplitter& operator++(int) {
+		return ++(*this);
+	}
+
+	// -----------------------------------------
+	/** get a pointer to the beginning of a particular token */
+	const char* operator[] (size_t idx) const {
+		const char* s = operator->()->c_str();
+
+		SkipSpaces(&s);
+		for(size_t i = 0; i < idx; ++i) {
+
+			for(;!IsSpace(*s); ++s) {
+				if(IsLineEnd(*s)) {
+					throw std::range_error("Token index out of range, EOL reached");
+				}
+			}
+			SkipSpaces(&s);
+		}
+		return s;
+	}
+
+	// -----------------------------------------
+	/** extract the start positions of N tokens from the current line*/
+	template <size_t N>
+	void get_tokens(const char* (&tokens)[N]) const {
+		const char* s = operator->()->c_str();
+
+		SkipSpaces(&s);
+		for(size_t i = 0; i < N; ++i) {
+			if(IsLineEnd(*s)) {
+
+				throw std::range_error("Token count out of range, EOL reached");
+
+			}
+			tokens[i] = s;
+
+			for(;*s && !IsSpace(*s); ++s);
+			SkipSpaces(&s);
+		}
+	}
+
+	// -----------------------------------------
+	/** member access */
+	const std::string* operator -> () const {
+		return &cur;
+	}
+
+	std::string operator* () const {
+		return cur;
+	}
+
+	// -----------------------------------------
+	/** boolean context */
+	operator bool() const {
+		return stream.GetRemainingSize()>0;
+	}
+
+	// -----------------------------------------
+	/** line indices are zero-based, empty lines are included */
+	operator line_idx() const {
+		return idx;
+	}
+
+	line_idx get_index() const {
+		return idx;
+	}
+
+	// -----------------------------------------
+	/** access the underlying stream object */
+	StreamReaderLE& get_stream() {
+		return stream;
+	}
+
+	// -----------------------------------------
+	/** !strcmp((*this)->substr(0,strlen(check)),check) */
+	bool match_start(const char* check) {
+		const size_t len = strlen(check);
+		
+		return len <= cur.length() && std::equal(check,check+len,cur.begin());
+	}
+
+
+	// -----------------------------------------
+	/** swallow the next call to ++, return the previous value. */
+	void swallow_next_increment() {
+		swallow = true;
+	}
+
+private:
+
+	line_idx idx;
+	std::string cur;
+	StreamReaderLE& stream;
+	bool swallow, skip_empty_lines, trim;
+};
+
+}
+#endif // INCLUDED_LINE_SPLITTER_H

+ 13 - 13
code/STEPFile.h

@@ -192,19 +192,19 @@ namespace STEP {
 			}
 			}
 
 
 			// utilities to deal with SELECT entities, which currently lack automatic
 			// utilities to deal with SELECT entities, which currently lack automatic
-			// conversion support.
-			template <typename T>
-			const T& ResolveSelect(const DB& db) const {
-				return Couple<T>(db).MustGetObject(To<EXPRESS::ENTITY>())->template To<T>();
-			}
-
-			template <typename T>
-			const T* ResolveSelectPtr(const DB& db) const {
-				const EXPRESS::ENTITY* e = ToPtr<EXPRESS::ENTITY>();
-				return e?Couple<T>(db).MustGetObject(*e)->template ToPtr<T>():(const T*)0;
-			}
-
-		public:
+			// conversion support.
+			template <typename T>
+			const T& ResolveSelect(const DB& db) const {
+				return Couple<T>(db).MustGetObject(To<EXPRESS::ENTITY>())->template To<T>();
+			}
+
+			template <typename T>
+			const T* ResolveSelectPtr(const DB& db) const {
+				const EXPRESS::ENTITY* e = ToPtr<EXPRESS::ENTITY>();
+				return e?Couple<T>(db).MustGetObject(*e)->template ToPtr<T>():(const T*)0;
+			}
+
+		public:
 
 
 			/** parse a variable from a string and set 'inout' to the character 
 			/** parse a variable from a string and set 'inout' to the character 
 			 *  behind the last consumed character. An optional schema enables,
 			 *  behind the last consumed character. An optional schema enables,

+ 4 - 18
code/STEPFileReader.cpp

@@ -175,7 +175,7 @@ bool IsEntityDef(const std::string& snext)
 			if (*it == '=') {
 			if (*it == '=') {
 				return true;
 				return true;
 			}
 			}
-			if (*it < '0' || *it > '9') {
+			if ((*it < '0' || *it > '9') && *it != ' ') {
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -197,16 +197,17 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 
 
 	const DB::ObjectMap& map = db.GetObjects();
 	const DB::ObjectMap& map = db.GetObjects();
 	LineSplitter& splitter = db.GetSplitter();
 	LineSplitter& splitter = db.GetSplitter();
+	
 	while (splitter) {
 	while (splitter) {
 		bool has_next = false;
 		bool has_next = false;
 		std::string s = *splitter;
 		std::string s = *splitter;
 		if (s == "ENDSEC;") {
 		if (s == "ENDSEC;") {
 			break;
 			break;
 		}
 		}
+		s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
 
 
 		// want one-based line numbers for human readers, so +1
 		// want one-based line numbers for human readers, so +1
 		const uint64_t line = splitter.get_index()+1;
 		const uint64_t line = splitter.get_index()+1;
-
 		// LineSplitter already ignores empty lines
 		// LineSplitter already ignores empty lines
 		ai_assert(s.length());
 		ai_assert(s.length());
 		if (s[0] != '#') {
 		if (s[0] != '#') {
@@ -214,12 +215,10 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 			++splitter;
 			++splitter;
 			continue;
 			continue;
 		}
 		}
-
 		// ---
 		// ---
 		// extract id, entity class name and argument string,
 		// extract id, entity class name and argument string,
 		// but don't create the actual object yet. 
 		// but don't create the actual object yet. 
 		// ---
 		// ---
-
 		const std::string::size_type n0 = s.find_first_of('=');
 		const std::string::size_type n0 = s.find_first_of('=');
 		if (n0 == std::string::npos) {
 		if (n0 == std::string::npos) {
 			DefaultLogger::get()->warn(AddLineNumber("expected token \'=\'",line));
 			DefaultLogger::get()->warn(AddLineNumber("expected token \'=\'",line));
@@ -233,13 +232,10 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 			++splitter;
 			++splitter;
 			continue;
 			continue;
 		}
 		}
-
 		std::string::size_type n1 = s.find_first_of('(',n0);
 		std::string::size_type n1 = s.find_first_of('(',n0);
 		if (n1 == std::string::npos) {
 		if (n1 == std::string::npos) {
-
 			has_next = true;
 			has_next = true;
 			bool ok = false;
 			bool ok = false;
-
 			for( ++splitter; splitter; ++splitter) {
 			for( ++splitter; splitter; ++splitter) {
 				const std::string& snext = *splitter;
 				const std::string& snext = *splitter;
 				if (snext.empty()) {
 				if (snext.empty()) {
@@ -269,13 +265,11 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 			
 			
 			has_next = true;
 			has_next = true;
 			bool ok = false;
 			bool ok = false;
-
 			for( ++splitter; splitter; ++splitter) {
 			for( ++splitter; splitter; ++splitter) {
 				const std::string& snext = *splitter;
 				const std::string& snext = *splitter;
 				if (snext.empty()) {
 				if (snext.empty()) {
 					continue;
 					continue;
 				}
 				}
-
 				// the next line doesn't start an entity, so maybe it is 
 				// the next line doesn't start an entity, so maybe it is 
 				// just a continuation  for this line, keep going
 				// just a continuation  for this line, keep going
 				if (!IsEntityDef(snext)) {
 				if (!IsEntityDef(snext)) {
@@ -287,7 +281,6 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 					break;
 					break;
 				}
 				}
 			}
 			}
-
 			if(!ok) {
 			if(!ok) {
 				DefaultLogger::get()->warn(AddLineNumber("expected token \')\'",line));
 				DefaultLogger::get()->warn(AddLineNumber("expected token \')\'",line));
 				continue;
 				continue;
@@ -300,24 +293,18 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 
 
 		std::string::size_type ns = n0;
 		std::string::size_type ns = n0;
 		do ++ns; while( IsSpace(s.at(ns)));
 		do ++ns; while( IsSpace(s.at(ns)));
-
 		std::string::size_type ne = n1;
 		std::string::size_type ne = n1;
 		do --ne; while( IsSpace(s.at(ne)));
 		do --ne; while( IsSpace(s.at(ne)));
-
 		std::string type = s.substr(ns,ne-ns+1);
 		std::string type = s.substr(ns,ne-ns+1);
 		std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char>  );
 		std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char>  );
-
 		const char* sz = scheme.GetStaticStringForToken(type);
 		const char* sz = scheme.GetStaticStringForToken(type);
 		if(sz) {
 		if(sz) {
-		
 			const std::string::size_type len = n2-n1+1;
 			const std::string::size_type len = n2-n1+1;
 			char* const copysz = new char[len+1];
 			char* const copysz = new char[len+1];
 			std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz);
 			std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz);
 			copysz[len] = '\0';
 			copysz[len] = '\0';
-
 			db.InternInsert(new LazyObject(db,id,line,sz,copysz));
 			db.InternInsert(new LazyObject(db,id,line,sz,copysz));
 		}
 		}
-
 		if(!has_next) {
 		if(!has_next) {
 			++splitter;
 			++splitter;
 		}
 		}
@@ -327,7 +314,7 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
 		DefaultLogger::get()->warn("STEP: ignoring unexpected EOF");
 		DefaultLogger::get()->warn("STEP: ignoring unexpected EOF");
 	}
 	}
 
 
-	if ( !DefaultLogger::isNullLogger() ){
+	if ( !DefaultLogger::isNullLogger()){
 		DefaultLogger::get()->debug((Formatter::format(),"STEP: got ",map.size()," object records with ",
 		DefaultLogger::get()->debug((Formatter::format(),"STEP: got ",map.size()," object records with ",
 			db.GetRefs().size()," inverse index entries"));
 			db.GetRefs().size()," inverse index entries"));
 	}
 	}
@@ -338,7 +325,6 @@ boost::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*&
 {
 {
 	const char* cur = inout;
 	const char* cur = inout;
 	SkipSpaces(&cur);
 	SkipSpaces(&cur);
-
 	if (*cur == ',' || IsSpaceOrNewLine(*cur)) {
 	if (*cur == ',' || IsSpaceOrNewLine(*cur)) {
 		throw STEP::SyntaxError("unexpected token, expected parameter",line);
 		throw STEP::SyntaxError("unexpected token, expected parameter",line);
 	}
 	}

+ 0 - 4
code/STEPFileReader.h

@@ -47,12 +47,10 @@ namespace Assimp {
 namespace STEP {
 namespace STEP {
 
 
 	// ### Parsing a STEP file is a twofold procedure ###
 	// ### Parsing a STEP file is a twofold procedure ###
-
 	// --------------------------------------------------------------------------
 	// --------------------------------------------------------------------------
 	// 1) read file header and return to caller, who checks if the 
 	// 1) read file header and return to caller, who checks if the 
 	//    file is of a supported schema ..
 	//    file is of a supported schema ..
 	DB* ReadFileHeader(boost::shared_ptr<IOStream> stream);
 	DB* ReadFileHeader(boost::shared_ptr<IOStream> stream);
-
 	// --------------------------------------------------------------------------
 	// --------------------------------------------------------------------------
 	// 2) read the actual file contents using a user-supplied set of
 	// 2) read the actual file contents using a user-supplied set of
 	//    conversion functions to interpret the data.
 	//    conversion functions to interpret the data.
@@ -60,8 +58,6 @@ namespace STEP {
 	template <size_t N, size_t N2> inline void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, const char* const (&arr)[N], const char* const (&arr2)[N2]) {
 	template <size_t N, size_t N2> inline void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, const char* const (&arr)[N], const char* const (&arr2)[N2]) {
 		return ReadFile(db,scheme,arr,N,arr2,N2);
 		return ReadFile(db,scheme,arr,N,arr2,N2);
 	}
 	}
-	
-
 } // ! STEP
 } // ! STEP
 } // ! Assimp
 } // ! Assimp