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