#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
#include <iterator>
#include <limits>
#include <tuple>
#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
# include <contrib/unzip/unzip.h>
#endif
#include "IFCLoader.h"
#include "STEPFileReader.h"
#include "IFCUtil.h"
#include "MemoryIOWrapper.h"
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
namespace Assimp {
template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
}
using namespace Assimp;
using namespace Assimp::Formatter;
using namespace Assimp::IFC;
namespace {
void SetUnits(ConversionData& conv);
void SetCoordinateSpace(ConversionData& conv);
void ProcessSpatialStructures(ConversionData& conv);
void MakeTreeRelative(ConversionData& conv);
void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv);
}
static const aiImporterDesc desc = {
"Industry Foundation Classes (IFC) Importer",
"",
"",
"",
aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"ifc ifczip stp"
};
IFCImporter::IFCImporter()
{}
IFCImporter::~IFCImporter()
{
}
bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string& extension = GetExtension(pFile);
if (extension == "ifc" || extension == "ifczip" || extension == "stp" ) {
return true;
} else if ((!extension.length() || checkSig) && pIOHandler) {
const char* tokens[] = {"ISO-10303-21"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
const aiImporterDesc* IFCImporter::GetInfo () const
{
return &desc;
}
void IFCImporter::SetupProperties(const Importer* pImp)
{
settings.skipSpaceRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS,true);
settings.useCustomTriangulation = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION,true);
settings.conicSamplingAngle = std::min(std::max((float) pImp->GetPropertyFloat(AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE, AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE), 5.0f), 120.0f);
settings.cylindricalTessellation = std::min(std::max(pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION, AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION), 3), 180);
settings.skipAnnotations = true;
}
void IFCImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile));
if (!stream) {
ThrowException("Could not open file for reading");
}
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");
}
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);
}
if(UNZ_OK == unzGoToFirstFile(zip)) {
do {
unz_file_info fileInfo;
char filename[256];
unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 );
if (GetExtension(filename) != "ifc") {
continue;
}
uint8_t* buff = new uint8_t[fileInfo.uncompressed_size];
LogInfo("Decompressing IFCZIP file");
unzOpenCurrentFile( zip );
const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size);
size_t filesize = fileInfo.uncompressed_size;
if ( ret < 0 || size_t(ret) != filesize )
{
delete[] buff;
ThrowException("Failed to decompress IFC ZIP file");
}
unzCloseCurrentFile( zip );
stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true));
break;
if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) {
ThrowException("Found no IFC file member in IFCZIP file (1)");
}
} while(true);
}
else {
ThrowException("Found no IFC file member in IFCZIP file (2)");
}
unzClose(zip);
#else
ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support");
#endif
}
std::unique_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 + '\'');
}
}
EXPRESS::ConversionSchema schema;
GetSchema(schema);
static const char* const types_to_track[] = {
"ifcsite", "ifcbuilding", "ifcproject"
};
static const char* const inverse_indices_to_track[] = {
"ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
};
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);
#ifdef ASSIMP_IFC_TEST
db->EvaluateAll();
#endif
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);
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);
conv.materials.clear();
}
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;
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&) {
IFCImporter::LogError("skipping unknown IfcUnit entry - expected entity");
}
}
void SetUnits(ConversionData& conv)
{
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;
for(const IfcRepresentationContext& v : conv.proj.RepresentationContexts) {
fav = &v;
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());
}
}
bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, unsigned int matid, ConversionData& conv)
{
std::unique_ptr<aiNode> nd(new aiNode());
nd->mName.Set("IfcMappedItem");
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();
for(TempOpening& open :*conv.apply_openings){
open.Transform(minv);
}
}
unsigned int localmatid = ProcessMaterials(mapped.GetID(),matid,conv,false);
const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
bool got = false;
for(const IfcRepresentationItem& item : repr.Items) {
if(!ProcessRepresentationItem(item,localmatid,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(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 {
if (! r->RepresentationIdentifier) {
return 0;
}
const std::string& name = r->RepresentationIdentifier.Get();
if (name == "MappedRepresentation") {
if (!r->Items.empty()) {
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;
}
if (r == "SweptSolid") {
return -10;
}
if (r == "Clipping") {
return -5;
}
if (r == "Brep") {
return -2;
}
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;
}
unsigned int matid = ProcessMaterials( el.GetID(), std::numeric_limits<uint32_t>::max(), conv, false);
std::vector<unsigned int> meshes;
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());
for(const IfcRepresentation* repr : repr_ordered) {
bool res = false;
for(const IfcRepresentationItem& item : repr->Items) {
if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
res = ProcessMappedItem(*geo,nd,subnodes,matid,conv) || res;
}
else {
res = ProcessRepresentationItem(item,matid,meshes,conv) || res;
}
}
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)
{
for(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;
for(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) { 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();
bool skipGeometry = false;
if(conv.settings.skipSpaceRepresentations) {
if(el.ToPtr<IfcSpace>()) {
IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings");
skipGeometry = true;
}
}
if(conv.settings.skipAnnotations) {
if(el.ToPtr<IfcAnnotation>()) {
IFCImporter::LogDebug("skipping IfcAnnotation entity due to importer settings");
return NULL;
}
}
std::unique_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());
STEP::DB::RefMapRange children = refs.equal_range(el.GetID());
if (children.first!=refs.end()) {
Metadata properties;
if (children.first==children.second) {
ProcessMetadata((*children.first).second, conv, properties);
}
else {
for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) {
ProcessMetadata((*it).second, conv, properties);
}
}
if (!properties.empty()) {
aiMetadata* data = aiMetadata::Alloc( static_cast<unsigned int>(properties.size()) );
unsigned int index( 0 );
for ( const Metadata::value_type& kv : properties ) {
data->Set( index++, kv.first, aiString( kv.second ) );
}
nd->mMetaData = data;
}
}
if(el.ObjectPlacement) {
ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv);
}
std::vector<TempOpening> openings;
IfcMatrix4 myInv;
bool didinv = false;
std::vector< aiNode* > subnodes;
try {
STEP::DB::RefMapRange range = refs.equal_range(el.GetID());
for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) {
if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) {
continue;
}
const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second);
if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) {
if(cont->RelatingStructure->GetID() != el.GetID()) {
continue;
}
for(const IfcProduct& pro : cont->RelatedElements) {
if(pro.ToPtr<IfcOpeningElement>()) {
continue;
}
aiNode* const ndnew = ProcessSpatialStructure(nd.get(),pro,conv,NULL);
if(ndnew) {
subnodes.push_back( ndnew );
}
}
}
else if(const IfcRelVoidsElement* const fills = obj->ToPtr<IfcRelVoidsElement>()) {
if(fills->RelatingBuildingElement->GetID() == el.GetID()) {
const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement;
std::unique_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;
}
for(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) {
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;
}
std::unique_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()]();
for(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;
}
if (!skipGeometry) {
ProcessProductRepresentation(el,nd.get(),subnodes,conv);
conv.apply_openings = conv.collect_openings = NULL;
}
if (subnodes.size()) {
nd->mChildren = new aiNode*[subnodes.size()]();
for(aiNode* nd2 : subnodes) {
nd->mChildren[nd->mNumChildren++] = nd2;
nd2->mParent = nd.get();
}
}
}
catch(...) {
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)
{
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()) {
IFCImporter::ThrowException("no root element found (expected IfcBuilding or preferably IfcSite)");
}
}
std::vector<aiNode*> nodes;
for(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():""));
const STEP::DB::RefMap& refs = conv.db.GetRefs();
STEP::DB::RefMapRange ref_range = refs.equal_range(conv.proj.GetID());
for(; ref_range.first != ref_range.second; ++ref_range.first) {
if(const IfcRelAggregates* const aggr = conv.db.GetObject((*ref_range.first).second)->ToPtr<IfcRelAggregates>()) {
for(const IfcObjectDefinition& def : aggr->RelatedObjects) {
if (def.GetID() == prod->GetID()) {
IFCImporter::LogDebug("selecting this spatial structure as root structure");
nodes.push_back(ProcessSpatialStructure(NULL, *prod, conv, NULL));
}
}
}
}
}
size_t nb_nodes = nodes.size();
if (nb_nodes == 0) {
IFCImporter::LogWarn("failed to determine primary site element, taking all the IfcSite");
for (const STEP::LazyObject* lz : *range) {
const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
if (!prod) {
continue;
}
nodes.push_back(ProcessSpatialStructure(NULL, *prod, conv, NULL));
}
nb_nodes = nodes.size();
}
if (nb_nodes == 1) {
conv.out->mRootNode = nodes[0];
}
else if (nb_nodes > 1) {
conv.out->mRootNode = new aiNode("Root");
conv.out->mRootNode->mParent = NULL;
conv.out->mRootNode->mNumChildren = static_cast<unsigned int>(nb_nodes);
conv.out->mRootNode->mChildren = new aiNode*[conv.out->mRootNode->mNumChildren];
for (size_t i = 0; i < nb_nodes; ++i) {
aiNode* node = nodes[i];
node->mParent = conv.out->mRootNode;
conv.out->mRootNode->mChildren[i] = node;
}
}
else {
IFCImporter::ThrowException("failed to determine primary site element");
}
}
void MakeTreeRelative(aiNode* start, const aiMatrix4x4& combined)
{
const aiMatrix4x4 old = start->mTransformation;
if (!combined.IsIdentity()) {
start->mTransformation = aiMatrix4x4(combined).Inverse() * start->mTransformation;
}
for (unsigned int i = 0; i < start->mNumChildren; ++i) {
MakeTreeRelative(start->mChildren[i],old);
}
}
void MakeTreeRelative(ConversionData& conv)
{
MakeTreeRelative(conv.out->mRootNode,IfcMatrix4());
}
}
#endif