vrshoot

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

looks nice :)
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 02 Feb 2014 00:35:22 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file DXFLoader.cpp
nuclear@0 43 * @brief Implementation of the DXF importer class
nuclear@0 44 */
nuclear@0 45
nuclear@0 46 #include "AssimpPCH.h"
nuclear@0 47 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
nuclear@0 48
nuclear@0 49 #include "DXFLoader.h"
nuclear@0 50 #include "ParsingUtils.h"
nuclear@0 51 #include "ConvertToLHProcess.h"
nuclear@0 52 #include "fast_atof.h"
nuclear@0 53
nuclear@0 54 #include "DXFHelper.h"
nuclear@0 55
nuclear@0 56 using namespace Assimp;
nuclear@0 57
nuclear@0 58 // AutoCAD Binary DXF<CR><LF><SUB><NULL>
nuclear@0 59 #define AI_DXF_BINARY_IDENT ("AutoCAD Binary DXF\r\n\x1a\0")
nuclear@0 60 #define AI_DXF_BINARY_IDENT_LEN (24)
nuclear@0 61
nuclear@0 62 // default vertex color that all uncolored vertices will receive
nuclear@0 63 #define AI_DXF_DEFAULT_COLOR aiColor4D(0.6f,0.6f,0.6f,0.6f)
nuclear@0 64
nuclear@0 65 // color indices for DXF - 16 are supported, the table is
nuclear@0 66 // taken directly from the DXF spec.
nuclear@0 67 static aiColor4D g_aclrDxfIndexColors[] =
nuclear@0 68 {
nuclear@0 69 aiColor4D (0.6f, 0.6f, 0.6f, 1.0f),
nuclear@0 70 aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
nuclear@0 71 aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
nuclear@0 72 aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
nuclear@0 73 aiColor4D (0.3f, 1.0f, 0.3f, 1.0f), // light green
nuclear@0 74 aiColor4D (0.3f, 0.3f, 1.0f, 1.0f), // light blue
nuclear@0 75 aiColor4D (1.0f, 0.3f, 0.3f, 1.0f), // light red
nuclear@0 76 aiColor4D (1.0f, 0.0f, 1.0f, 1.0f), // pink
nuclear@0 77 aiColor4D (1.0f, 0.6f, 0.0f, 1.0f), // orange
nuclear@0 78 aiColor4D (0.6f, 0.3f, 0.0f, 1.0f), // dark orange
nuclear@0 79 aiColor4D (1.0f, 1.0f, 0.0f, 1.0f), // yellow
nuclear@0 80 aiColor4D (0.3f, 0.3f, 0.3f, 1.0f), // dark gray
nuclear@0 81 aiColor4D (0.8f, 0.8f, 0.8f, 1.0f), // light gray
nuclear@0 82 aiColor4D (0.0f, 00.f, 0.0f, 1.0f), // black
nuclear@0 83 aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
nuclear@0 84 aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
nuclear@0 85 };
nuclear@0 86 #define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
nuclear@0 87 #define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC"
nuclear@0 88
nuclear@0 89
nuclear@0 90 static const aiImporterDesc desc = {
nuclear@0 91 "Drawing Interchange Format (DXF) Importer",
nuclear@0 92 "",
nuclear@0 93 "",
nuclear@0 94 "",
nuclear@0 95 aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport,
nuclear@0 96 0,
nuclear@0 97 0,
nuclear@0 98 0,
nuclear@0 99 0,
nuclear@0 100 "dxf"
nuclear@0 101 };
nuclear@0 102
nuclear@0 103 // ------------------------------------------------------------------------------------------------
nuclear@0 104 // Constructor to be privately used by Importer
nuclear@0 105 DXFImporter::DXFImporter()
nuclear@0 106 {}
nuclear@0 107
nuclear@0 108 // ------------------------------------------------------------------------------------------------
nuclear@0 109 // Destructor, private as well
nuclear@0 110 DXFImporter::~DXFImporter()
nuclear@0 111 {}
nuclear@0 112
nuclear@0 113 // ------------------------------------------------------------------------------------------------
nuclear@0 114 // Returns whether the class can handle the format of the given file.
nuclear@0 115 bool DXFImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
nuclear@0 116 {
nuclear@0 117 return SimpleExtensionCheck(pFile,"dxf");
nuclear@0 118 }
nuclear@0 119
nuclear@0 120 // ------------------------------------------------------------------------------------------------
nuclear@0 121 // Get a list of all supported file extensions
nuclear@0 122 const aiImporterDesc* DXFImporter::GetInfo () const
nuclear@0 123 {
nuclear@0 124 return &desc;
nuclear@0 125 }
nuclear@0 126
nuclear@0 127 // ------------------------------------------------------------------------------------------------
nuclear@0 128 // Imports the given file into the given scene structure.
nuclear@0 129 void DXFImporter::InternReadFile( const std::string& pFile,
nuclear@0 130 aiScene* pScene,
nuclear@0 131 IOSystem* pIOHandler)
nuclear@0 132 {
nuclear@0 133 boost::shared_ptr<IOStream> file = boost::shared_ptr<IOStream>( pIOHandler->Open( pFile) );
nuclear@0 134
nuclear@0 135 // Check whether we can read the file
nuclear@0 136 if( file.get() == NULL) {
nuclear@0 137 throw DeadlyImportError( "Failed to open DXF file " + pFile + "");
nuclear@0 138 }
nuclear@0 139
nuclear@0 140 // check whether this is a binaray DXF file - we can't read binary DXF files :-(
nuclear@0 141 char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0};
nuclear@0 142 file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1);
nuclear@0 143
nuclear@0 144 if (!strncmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) {
nuclear@0 145 throw DeadlyImportError("DXF: Binary files are not supported at the moment");
nuclear@0 146 }
nuclear@0 147
nuclear@0 148 // DXF files can grow very large, so read them via the StreamReader,
nuclear@0 149 // which will choose a suitable strategy.
nuclear@0 150 file->Seek(0,aiOrigin_SET);
nuclear@0 151 StreamReaderLE stream( file );
nuclear@0 152
nuclear@0 153 DXF::LineReader reader (stream);
nuclear@0 154 DXF::FileData output;
nuclear@0 155
nuclear@0 156 // now get all lines of the file and process top-level sections
nuclear@0 157 bool eof = false;
nuclear@0 158 while(!reader.End()) {
nuclear@0 159
nuclear@0 160 // blocks table - these 'build blocks' are later (in ENTITIES)
nuclear@0 161 // referenced an included via INSERT statements.
nuclear@0 162 if (reader.Is(2,"BLOCKS")) {
nuclear@0 163 ParseBlocks(reader,output);
nuclear@0 164 continue;
nuclear@0 165 }
nuclear@0 166
nuclear@0 167 // primary entity table
nuclear@0 168 if (reader.Is(2,"ENTITIES")) {
nuclear@0 169 ParseEntities(reader,output);
nuclear@0 170 continue;
nuclear@0 171 }
nuclear@0 172
nuclear@0 173 // skip unneeded sections entirely to avoid any problems with them
nuclear@0 174 // alltogether.
nuclear@0 175 else if (reader.Is(2,"CLASSES") || reader.Is(2,"TABLES")) {
nuclear@0 176 SkipSection(reader);
nuclear@0 177 continue;
nuclear@0 178 }
nuclear@0 179
nuclear@0 180 else if (reader.Is(2,"HEADER")) {
nuclear@0 181 ParseHeader(reader,output);
nuclear@0 182 continue;
nuclear@0 183 }
nuclear@0 184
nuclear@0 185 // comments
nuclear@0 186 else if (reader.Is(999)) {
nuclear@0 187 DefaultLogger::get()->info("DXF Comment: " + reader.Value());
nuclear@0 188 }
nuclear@0 189
nuclear@0 190 // don't read past the official EOF sign
nuclear@0 191 else if (reader.Is(0,"EOF")) {
nuclear@0 192 eof = true;
nuclear@0 193 break;
nuclear@0 194 }
nuclear@0 195
nuclear@0 196 ++reader;
nuclear@0 197 }
nuclear@0 198 if (!eof) {
nuclear@0 199 DefaultLogger::get()->warn("DXF: EOF reached, but did not encounter DXF EOF marker");
nuclear@0 200 }
nuclear@0 201
nuclear@0 202 ConvertMeshes(pScene,output);
nuclear@0 203
nuclear@0 204 // Now rotate the whole scene by 90 degrees around the x axis to convert from AutoCAD's to Assimp's coordinate system
nuclear@0 205 pScene->mRootNode->mTransformation = aiMatrix4x4(
nuclear@0 206 1.f,0.f,0.f,0.f,
nuclear@0 207 0.f,0.f,1.f,0.f,
nuclear@0 208 0.f,-1.f,0.f,0.f,
nuclear@0 209 0.f,0.f,0.f,1.f) * pScene->mRootNode->mTransformation;
nuclear@0 210 }
nuclear@0 211
nuclear@0 212 // ------------------------------------------------------------------------------------------------
nuclear@0 213 void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output)
nuclear@0 214 {
nuclear@0 215 // the process of resolving all the INSERT statements can grow the
nuclear@0 216 // polycount excessively, so log the original number.
nuclear@0 217 // XXX Option to import blocks as separate nodes?
nuclear@0 218 if (!DefaultLogger::isNullLogger()) {
nuclear@0 219
nuclear@0 220 unsigned int vcount = 0, icount = 0;
nuclear@0 221 BOOST_FOREACH (const DXF::Block& bl, output.blocks) {
nuclear@0 222 BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl, bl.lines) {
nuclear@0 223 vcount += pl->positions.size();
nuclear@0 224 icount += pl->counts.size();
nuclear@0 225 }
nuclear@0 226 }
nuclear@0 227
nuclear@0 228 DefaultLogger::get()->debug((Formatter::format("DXF: Unexpanded polycount is "),
nuclear@0 229 icount,", vertex count is ",vcount
nuclear@0 230 ));
nuclear@0 231 }
nuclear@0 232
nuclear@0 233 if (! output.blocks.size() ) {
nuclear@0 234 throw DeadlyImportError("DXF: no data blocks loaded");
nuclear@0 235 }
nuclear@0 236
nuclear@0 237 DXF::Block* entities = 0;
nuclear@0 238
nuclear@0 239 // index blocks by name
nuclear@0 240 DXF::BlockMap blocks_by_name;
nuclear@0 241 BOOST_FOREACH (DXF::Block& bl, output.blocks) {
nuclear@0 242 blocks_by_name[bl.name] = &bl;
nuclear@0 243 if ( !entities && bl.name == AI_DXF_ENTITIES_MAGIC_BLOCK ) {
nuclear@0 244 entities = &bl;
nuclear@0 245 }
nuclear@0 246 }
nuclear@0 247
nuclear@0 248 if (!entities) {
nuclear@0 249 throw DeadlyImportError("DXF: no ENTITIES data block loaded");
nuclear@0 250 }
nuclear@0 251
nuclear@0 252 typedef std::map<std::string, unsigned int> LayerMap;
nuclear@0 253
nuclear@0 254 LayerMap layers;
nuclear@0 255 std::vector< std::vector< const DXF::PolyLine*> > corr;
nuclear@0 256
nuclear@0 257 // now expand all block references in the primary ENTITIES block
nuclear@0 258 // XXX this involves heavy memory copying, consider a faster solution for future versions.
nuclear@0 259 ExpandBlockReferences(*entities,blocks_by_name);
nuclear@0 260
nuclear@0 261 unsigned int cur = 0;
nuclear@0 262 BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl, entities->lines) {
nuclear@0 263 if (pl->positions.size()) {
nuclear@0 264
nuclear@0 265 std::map<std::string, unsigned int>::iterator it = layers.find(pl->layer);
nuclear@0 266 if (it == layers.end()) {
nuclear@0 267 ++pScene->mNumMeshes;
nuclear@0 268
nuclear@0 269 layers[pl->layer] = cur++;
nuclear@0 270
nuclear@0 271 std::vector< const DXF::PolyLine* > pv;
nuclear@0 272 pv.push_back(&*pl);
nuclear@0 273
nuclear@0 274 corr.push_back(pv);
nuclear@0 275 }
nuclear@0 276 else {
nuclear@0 277 corr[(*it).second].push_back(&*pl);
nuclear@0 278 }
nuclear@0 279 }
nuclear@0 280 }
nuclear@0 281
nuclear@0 282 if (!pScene->mNumMeshes) {
nuclear@0 283 throw DeadlyImportError("DXF: this file contains no 3d data");
nuclear@0 284 }
nuclear@0 285
nuclear@0 286 pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ] ();
nuclear@0 287
nuclear@0 288 BOOST_FOREACH(const LayerMap::value_type& elem, layers){
nuclear@0 289 aiMesh* const mesh = pScene->mMeshes[elem.second] = new aiMesh();
nuclear@0 290 mesh->mName.Set(elem.first);
nuclear@0 291
nuclear@0 292 unsigned int cvert = 0,cface = 0;
nuclear@0 293 BOOST_FOREACH(const DXF::PolyLine* pl, corr[elem.second]){
nuclear@0 294 // sum over all faces since we need to 'verbosify' them.
nuclear@0 295 cvert += std::accumulate(pl->counts.begin(),pl->counts.end(),0);
nuclear@0 296 cface += pl->counts.size();
nuclear@0 297 }
nuclear@0 298
nuclear@0 299 aiVector3D* verts = mesh->mVertices = new aiVector3D[cvert];
nuclear@0 300 aiColor4D* colors = mesh->mColors[0] = new aiColor4D[cvert];
nuclear@0 301 aiFace* faces = mesh->mFaces = new aiFace[cface];
nuclear@0 302
nuclear@0 303 mesh->mNumVertices = cvert;
nuclear@0 304 mesh->mNumFaces = cface;
nuclear@0 305
nuclear@0 306 unsigned int prims = 0;
nuclear@0 307 unsigned int overall_indices = 0;
nuclear@0 308 BOOST_FOREACH(const DXF::PolyLine* pl, corr[elem.second]){
nuclear@0 309
nuclear@0 310 std::vector<unsigned int>::const_iterator it = pl->indices.begin();
nuclear@0 311 BOOST_FOREACH(unsigned int facenumv,pl->counts) {
nuclear@0 312 aiFace& face = *faces++;
nuclear@0 313 face.mIndices = new unsigned int[face.mNumIndices = facenumv];
nuclear@0 314
nuclear@0 315 for (unsigned int i = 0; i < facenumv; ++i) {
nuclear@0 316 face.mIndices[i] = overall_indices++;
nuclear@0 317
nuclear@0 318 ai_assert(pl->positions.size() == pl->colors.size());
nuclear@0 319 if (*it >= pl->positions.size()) {
nuclear@0 320 throw DeadlyImportError("DXF: vertex index out of bounds");
nuclear@0 321 }
nuclear@0 322
nuclear@0 323 *verts++ = pl->positions[*it];
nuclear@0 324 *colors++ = pl->colors[*it++];
nuclear@0 325 }
nuclear@0 326
nuclear@0 327 // set primitive flags now, this saves the extra pass in ScenePreprocessor.
nuclear@0 328 switch(face.mNumIndices) {
nuclear@0 329 case 1:
nuclear@0 330 prims |= aiPrimitiveType_POINT;
nuclear@0 331 break;
nuclear@0 332 case 2:
nuclear@0 333 prims |= aiPrimitiveType_LINE;
nuclear@0 334 break;
nuclear@0 335 case 3:
nuclear@0 336 prims |= aiPrimitiveType_TRIANGLE;
nuclear@0 337 break;
nuclear@0 338 default:
nuclear@0 339 prims |= aiPrimitiveType_POLYGON;
nuclear@0 340 break;
nuclear@0 341 }
nuclear@0 342 }
nuclear@0 343 }
nuclear@0 344
nuclear@0 345 mesh->mPrimitiveTypes = prims;
nuclear@0 346 mesh->mMaterialIndex = 0;
nuclear@0 347 }
nuclear@0 348
nuclear@0 349 GenerateHierarchy(pScene,output);
nuclear@0 350 GenerateMaterials(pScene,output);
nuclear@0 351 }
nuclear@0 352
nuclear@0 353
nuclear@0 354 // ------------------------------------------------------------------------------------------------
nuclear@0 355 void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& blocks_by_name)
nuclear@0 356 {
nuclear@0 357 BOOST_FOREACH (const DXF::InsertBlock& insert, bl.insertions) {
nuclear@0 358
nuclear@0 359 // first check if the referenced blocks exists ...
nuclear@0 360 const DXF::BlockMap::const_iterator it = blocks_by_name.find(insert.name);
nuclear@0 361 if (it == blocks_by_name.end()) {
nuclear@0 362 DefaultLogger::get()->error((Formatter::format("DXF: Failed to resolve block reference: "),
nuclear@0 363 insert.name,"; skipping"
nuclear@0 364 ));
nuclear@0 365 continue;
nuclear@0 366 }
nuclear@0 367
nuclear@0 368 // XXX this would be the place to implement recursive expansion if needed.
nuclear@0 369 const DXF::Block& bl_src = *(*it).second;
nuclear@0 370
nuclear@0 371 BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl_in, bl_src.lines) {
nuclear@0 372 boost::shared_ptr<DXF::PolyLine> pl_out = boost::shared_ptr<DXF::PolyLine>(new DXF::PolyLine(*pl_in));
nuclear@0 373
nuclear@0 374 if (bl_src.base.Length() || insert.scale.x!=1.f || insert.scale.y!=1.f || insert.scale.z!=1.f || insert.angle || insert.pos.Length()) {
nuclear@0 375 // manual coordinate system transformation
nuclear@0 376 // XXX order
nuclear@0 377 aiMatrix4x4 trafo, tmp;
nuclear@0 378 aiMatrix4x4::Translation(-bl_src.base,trafo);
nuclear@0 379 trafo *= aiMatrix4x4::Scaling(insert.scale,tmp);
nuclear@0 380 trafo *= aiMatrix4x4::Translation(insert.pos,tmp);
nuclear@0 381
nuclear@0 382 // XXX rotation currently ignored - I didn't find an appropriate sample model.
nuclear@0 383 if (insert.angle != 0.f) {
nuclear@0 384 DefaultLogger::get()->warn("DXF: BLOCK rotation not currently implemented");
nuclear@0 385 }
nuclear@0 386
nuclear@0 387 BOOST_FOREACH (aiVector3D& v, pl_out->positions) {
nuclear@0 388 v *= trafo;
nuclear@0 389 }
nuclear@0 390 }
nuclear@0 391
nuclear@0 392 bl.lines.push_back(pl_out);
nuclear@0 393 }
nuclear@0 394 }
nuclear@0 395 }
nuclear@0 396
nuclear@0 397
nuclear@0 398 // ------------------------------------------------------------------------------------------------
nuclear@0 399 void DXFImporter::GenerateMaterials(aiScene* pScene, DXF::FileData& /*output*/)
nuclear@0 400 {
nuclear@0 401 // generate an almost-white default material. Reason:
nuclear@0 402 // the default vertex color is GREY, so we are
nuclear@0 403 // already at Assimp's usual default color.
nuclear@0 404 // generate a default material
nuclear@0 405 aiMaterial* pcMat = new aiMaterial();
nuclear@0 406 aiString s;
nuclear@0 407 s.Set(AI_DEFAULT_MATERIAL_NAME);
nuclear@0 408 pcMat->AddProperty(&s, AI_MATKEY_NAME);
nuclear@0 409
nuclear@0 410 aiColor4D clrDiffuse(0.9f,0.9f,0.9f,1.0f);
nuclear@0 411 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
nuclear@0 412
nuclear@0 413 clrDiffuse = aiColor4D(1.0f,1.0f,1.0f,1.0f);
nuclear@0 414 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
nuclear@0 415
nuclear@0 416 clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
nuclear@0 417 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
nuclear@0 418
nuclear@0 419 pScene->mNumMaterials = 1;
nuclear@0 420 pScene->mMaterials = new aiMaterial*[1];
nuclear@0 421 pScene->mMaterials[0] = pcMat;
nuclear@0 422 }
nuclear@0 423
nuclear@0 424
nuclear@0 425 // ------------------------------------------------------------------------------------------------
nuclear@0 426 void DXFImporter::GenerateHierarchy(aiScene* pScene, DXF::FileData& /*output*/)
nuclear@0 427 {
nuclear@0 428 // generate the output scene graph, which is just the root node with a single child for each layer.
nuclear@0 429 pScene->mRootNode = new aiNode();
nuclear@0 430 pScene->mRootNode->mName.Set("<DXF_ROOT>");
nuclear@0 431
nuclear@0 432 if (1 == pScene->mNumMeshes) {
nuclear@0 433 pScene->mRootNode->mMeshes = new unsigned int[ pScene->mRootNode->mNumMeshes = 1 ];
nuclear@0 434 pScene->mRootNode->mMeshes[0] = 0;
nuclear@0 435 }
nuclear@0 436 else
nuclear@0 437 {
nuclear@0 438 pScene->mRootNode->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren = pScene->mNumMeshes ];
nuclear@0 439 for (unsigned int m = 0; m < pScene->mRootNode->mNumChildren;++m) {
nuclear@0 440 aiNode* p = pScene->mRootNode->mChildren[m] = new aiNode();
nuclear@0 441 p->mName = pScene->mMeshes[m]->mName;
nuclear@0 442
nuclear@0 443 p->mMeshes = new unsigned int[p->mNumMeshes = 1];
nuclear@0 444 p->mMeshes[0] = m;
nuclear@0 445 p->mParent = pScene->mRootNode;
nuclear@0 446 }
nuclear@0 447 }
nuclear@0 448 }
nuclear@0 449
nuclear@0 450
nuclear@0 451 // ------------------------------------------------------------------------------------------------
nuclear@0 452 void DXFImporter::SkipSection(DXF::LineReader& reader)
nuclear@0 453 {
nuclear@0 454 for( ;!reader.End() && !reader.Is(0,"ENDSEC"); reader++);
nuclear@0 455 }
nuclear@0 456
nuclear@0 457
nuclear@0 458 // ------------------------------------------------------------------------------------------------
nuclear@0 459 void DXFImporter::ParseHeader(DXF::LineReader& reader, DXF::FileData& /*output*/)
nuclear@0 460 {
nuclear@0 461 for( ;!reader.End() && !reader.Is(0,"ENDSEC"); reader++);
nuclear@0 462 }
nuclear@0 463
nuclear@0 464
nuclear@0 465 // ------------------------------------------------------------------------------------------------
nuclear@0 466 void DXFImporter::ParseBlocks(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 467 {
nuclear@0 468 while( !reader.End() && !reader.Is(0,"ENDSEC")) {
nuclear@0 469 if (reader.Is(0,"BLOCK")) {
nuclear@0 470 ParseBlock(++reader,output);
nuclear@0 471 continue;
nuclear@0 472 }
nuclear@0 473 ++reader;
nuclear@0 474 }
nuclear@0 475
nuclear@0 476 DefaultLogger::get()->debug((Formatter::format("DXF: got "),
nuclear@0 477 output.blocks.size()," entries in BLOCKS"
nuclear@0 478 ));
nuclear@0 479 }
nuclear@0 480
nuclear@0 481
nuclear@0 482 // ------------------------------------------------------------------------------------------------
nuclear@0 483 void DXFImporter::ParseBlock(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 484 {
nuclear@0 485 // push a new block onto the stack.
nuclear@0 486 output.blocks.push_back( DXF::Block() );
nuclear@0 487 DXF::Block& block = output.blocks.back();
nuclear@0 488
nuclear@0 489 while( !reader.End() && !reader.Is(0,"ENDBLK")) {
nuclear@0 490
nuclear@0 491 switch(reader.GroupCode()) {
nuclear@0 492 case 2:
nuclear@0 493 block.name = reader.Value();
nuclear@0 494 break;
nuclear@0 495
nuclear@0 496 case 10:
nuclear@0 497 block.base.x = reader.ValueAsFloat();
nuclear@0 498 break;
nuclear@0 499 case 20:
nuclear@0 500 block.base.y = reader.ValueAsFloat();
nuclear@0 501 break;
nuclear@0 502 case 30:
nuclear@0 503 block.base.z = reader.ValueAsFloat();
nuclear@0 504 break;
nuclear@0 505 }
nuclear@0 506
nuclear@0 507 if (reader.Is(0,"POLYLINE")) {
nuclear@0 508 ParsePolyLine(++reader,output);
nuclear@0 509 continue;
nuclear@0 510 }
nuclear@0 511
nuclear@0 512 // XXX is this a valid case?
nuclear@0 513 if (reader.Is(0,"INSERT")) {
nuclear@0 514 DefaultLogger::get()->warn("DXF: INSERT within a BLOCK not currently supported; skipping");
nuclear@0 515 for( ;!reader.End() && !reader.Is(0,"ENDBLK"); ++reader);
nuclear@0 516 break;
nuclear@0 517 }
nuclear@0 518
nuclear@0 519 else if (reader.Is(0,"3DFACE") || reader.Is(0,"LINE") || reader.Is(0,"3DLINE")) {
nuclear@0 520 //http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
nuclear@0 521 Parse3DFace(++reader, output);
nuclear@0 522 continue;
nuclear@0 523 }
nuclear@0 524 ++reader;
nuclear@0 525 }
nuclear@0 526 }
nuclear@0 527
nuclear@0 528
nuclear@0 529 // ------------------------------------------------------------------------------------------------
nuclear@0 530 void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 531 {
nuclear@0 532 // push a new block onto the stack.
nuclear@0 533 output.blocks.push_back( DXF::Block() );
nuclear@0 534 DXF::Block& block = output.blocks.back();
nuclear@0 535
nuclear@0 536 block.name = AI_DXF_ENTITIES_MAGIC_BLOCK;
nuclear@0 537
nuclear@0 538 while( !reader.End() && !reader.Is(0,"ENDSEC")) {
nuclear@0 539 if (reader.Is(0,"POLYLINE")) {
nuclear@0 540 ParsePolyLine(++reader,output);
nuclear@0 541 continue;
nuclear@0 542 }
nuclear@0 543
nuclear@0 544 else if (reader.Is(0,"INSERT")) {
nuclear@0 545 ParseInsertion(++reader,output);
nuclear@0 546 continue;
nuclear@0 547 }
nuclear@0 548
nuclear@0 549 else if (reader.Is(0,"3DFACE") || reader.Is(0,"LINE") || reader.Is(0,"3DLINE")) {
nuclear@0 550 //http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
nuclear@0 551 Parse3DFace(++reader, output);
nuclear@0 552 continue;
nuclear@0 553 }
nuclear@0 554
nuclear@0 555 ++reader;
nuclear@0 556 }
nuclear@0 557
nuclear@0 558 DefaultLogger::get()->debug((Formatter::format("DXF: got "),
nuclear@0 559 block.lines.size()," polylines and ", block.insertions.size() ," inserted blocks in ENTITIES"
nuclear@0 560 ));
nuclear@0 561 }
nuclear@0 562
nuclear@0 563
nuclear@0 564 void DXFImporter::ParseInsertion(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 565 {
nuclear@0 566 output.blocks.back().insertions.push_back( DXF::InsertBlock() );
nuclear@0 567 DXF::InsertBlock& bl = output.blocks.back().insertions.back();
nuclear@0 568
nuclear@0 569 while( !reader.End() && !reader.Is(0)) {
nuclear@0 570
nuclear@0 571 switch(reader.GroupCode())
nuclear@0 572 {
nuclear@0 573 // name of referenced block
nuclear@0 574 case 2:
nuclear@0 575 bl.name = reader.Value();
nuclear@0 576 break;
nuclear@0 577
nuclear@0 578 // translation
nuclear@0 579 case 10:
nuclear@0 580 bl.pos.x = reader.ValueAsFloat();
nuclear@0 581 break;
nuclear@0 582 case 20:
nuclear@0 583 bl.pos.y = reader.ValueAsFloat();
nuclear@0 584 break;
nuclear@0 585 case 30:
nuclear@0 586 bl.pos.z = reader.ValueAsFloat();
nuclear@0 587 break;
nuclear@0 588
nuclear@0 589 // scaling
nuclear@0 590 case 41:
nuclear@0 591 bl.scale.x = reader.ValueAsFloat();
nuclear@0 592 break;
nuclear@0 593 case 42:
nuclear@0 594 bl.scale.y = reader.ValueAsFloat();
nuclear@0 595 break;
nuclear@0 596 case 43:
nuclear@0 597 bl.scale.z = reader.ValueAsFloat();
nuclear@0 598 break;
nuclear@0 599
nuclear@0 600 // rotation angle
nuclear@0 601 case 50:
nuclear@0 602 bl.angle = reader.ValueAsFloat();
nuclear@0 603 break;
nuclear@0 604 }
nuclear@0 605 reader++;
nuclear@0 606 }
nuclear@0 607 }
nuclear@0 608
nuclear@0 609 #define DXF_POLYLINE_FLAG_CLOSED 0x1
nuclear@0 610 #define DXF_POLYLINE_FLAG_3D_POLYLINE 0x8
nuclear@0 611 #define DXF_POLYLINE_FLAG_3D_POLYMESH 0x10
nuclear@0 612 #define DXF_POLYLINE_FLAG_POLYFACEMESH 0x40
nuclear@0 613
nuclear@0 614 // ------------------------------------------------------------------------------------------------
nuclear@0 615 void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 616 {
nuclear@0 617 output.blocks.back().lines.push_back( boost::shared_ptr<DXF::PolyLine>( new DXF::PolyLine() ) );
nuclear@0 618 DXF::PolyLine& line = *output.blocks.back().lines.back();
nuclear@0 619
nuclear@0 620 unsigned int iguess = 0, vguess = 0;
nuclear@0 621 while( !reader.End() && !reader.Is(0,"ENDSEC")) {
nuclear@0 622
nuclear@0 623 if (reader.Is(0,"VERTEX")) {
nuclear@0 624 ParsePolyLineVertex(++reader,line);
nuclear@0 625 if (reader.Is(0,"SEQEND")) {
nuclear@0 626 break;
nuclear@0 627 }
nuclear@0 628 continue;
nuclear@0 629 }
nuclear@0 630
nuclear@0 631 switch(reader.GroupCode())
nuclear@0 632 {
nuclear@0 633 // flags --- important that we know whether it is a
nuclear@0 634 // polyface mesh or 'just' a line.
nuclear@0 635 case 70:
nuclear@0 636 if (!line.flags) {
nuclear@0 637 line.flags = reader.ValueAsSignedInt();
nuclear@0 638 }
nuclear@0 639 break;
nuclear@0 640
nuclear@0 641 // optional number of vertices
nuclear@0 642 case 71:
nuclear@0 643 vguess = reader.ValueAsSignedInt();
nuclear@0 644 line.positions.reserve(vguess);
nuclear@0 645 break;
nuclear@0 646
nuclear@0 647 // optional number of faces
nuclear@0 648 case 72:
nuclear@0 649 iguess = reader.ValueAsSignedInt();
nuclear@0 650 line.indices.reserve(iguess);
nuclear@0 651 break;
nuclear@0 652
nuclear@0 653 // 8 specifies the layer on which this line is placed on
nuclear@0 654 case 8:
nuclear@0 655 line.layer = reader.Value();
nuclear@0 656 break;
nuclear@0 657 }
nuclear@0 658
nuclear@0 659 reader++;
nuclear@0 660 }
nuclear@0 661
nuclear@0 662 //if (!(line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH)) {
nuclear@0 663 // DefaultLogger::get()->warn((Formatter::format("DXF: polyline not currently supported: "),line.flags));
nuclear@0 664 // output.blocks.back().lines.pop_back();
nuclear@0 665 // return;
nuclear@0 666 //}
nuclear@0 667
nuclear@0 668 if (vguess && line.positions.size() != vguess) {
nuclear@0 669 DefaultLogger::get()->warn((Formatter::format("DXF: unexpected vertex count in polymesh: "),
nuclear@0 670 line.positions.size(),", expected ", vguess
nuclear@0 671 ));
nuclear@0 672 }
nuclear@0 673
nuclear@0 674 if (line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH ) {
nuclear@0 675 if (line.positions.size() < 3 || line.indices.size() < 3) {
nuclear@0 676 DefaultLogger::get()->warn("DXF: not enough vertices for polymesh; ignoring");
nuclear@0 677 output.blocks.back().lines.pop_back();
nuclear@0 678 return;
nuclear@0 679 }
nuclear@0 680
nuclear@0 681 // if these numbers are wrong, parsing might have gone wild.
nuclear@0 682 // however, the docs state that applications are not required
nuclear@0 683 // to set the 71 and 72 fields, respectively, to valid values.
nuclear@0 684 // So just fire a warning.
nuclear@0 685 if (iguess && line.counts.size() != iguess) {
nuclear@0 686 DefaultLogger::get()->warn((Formatter::format("DXF: unexpected face count in polymesh: "),
nuclear@0 687 line.counts.size(),", expected ", iguess
nuclear@0 688 ));
nuclear@0 689 }
nuclear@0 690 }
nuclear@0 691 else if (!line.indices.size() && !line.counts.size()) {
nuclear@0 692 // a polyline - so there are no indices yet.
nuclear@0 693 size_t guess = line.positions.size() + (line.flags & DXF_POLYLINE_FLAG_CLOSED ? 1 : 0);
nuclear@0 694 line.indices.reserve(guess);
nuclear@0 695
nuclear@0 696 line.counts.reserve(guess/2);
nuclear@0 697 for (unsigned int i = 0; i < line.positions.size()/2; ++i) {
nuclear@0 698 line.indices.push_back(i*2);
nuclear@0 699 line.indices.push_back(i*2+1);
nuclear@0 700 line.counts.push_back(2);
nuclear@0 701 }
nuclear@0 702
nuclear@0 703 // closed polyline?
nuclear@0 704 if (line.flags & DXF_POLYLINE_FLAG_CLOSED) {
nuclear@0 705 line.indices.push_back(line.positions.size()-1);
nuclear@0 706 line.indices.push_back(0);
nuclear@0 707 line.counts.push_back(2);
nuclear@0 708 }
nuclear@0 709 }
nuclear@0 710 }
nuclear@0 711
nuclear@0 712 #define DXF_VERTEX_FLAG_PART_OF_POLYFACE 0x80
nuclear@0 713 #define DXF_VERTEX_FLAG_HAS_POSITIONS 0x40
nuclear@0 714
nuclear@0 715 // ------------------------------------------------------------------------------------------------
nuclear@0 716 void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& line)
nuclear@0 717 {
nuclear@0 718 unsigned int cnti = 0, flags = 0;
nuclear@0 719 unsigned int indices[4];
nuclear@0 720
nuclear@0 721 aiVector3D out;
nuclear@0 722 aiColor4D clr = AI_DXF_DEFAULT_COLOR;
nuclear@0 723
nuclear@0 724 while( !reader.End() ) {
nuclear@0 725
nuclear@0 726 if (reader.Is(0)) { // SEQEND or another VERTEX
nuclear@0 727 break;
nuclear@0 728 }
nuclear@0 729
nuclear@0 730 switch (reader.GroupCode())
nuclear@0 731 {
nuclear@0 732 case 8:
nuclear@0 733 // layer to which the vertex belongs to - assume that
nuclear@0 734 // this is always the layer the top-level polyline
nuclear@0 735 // entity resides on as well.
nuclear@0 736 if(reader.Value() != line.layer) {
nuclear@0 737 DefaultLogger::get()->warn("DXF: expected vertex to be part of a polyface but the 0x128 flag isn't set");
nuclear@0 738 }
nuclear@0 739 break;
nuclear@0 740
nuclear@0 741 case 70:
nuclear@0 742 flags = reader.ValueAsUnsignedInt();
nuclear@0 743 break;
nuclear@0 744
nuclear@0 745 // VERTEX COORDINATES
nuclear@0 746 case 10: out.x = reader.ValueAsFloat();break;
nuclear@0 747 case 20: out.y = reader.ValueAsFloat();break;
nuclear@0 748 case 30: out.z = reader.ValueAsFloat();break;
nuclear@0 749
nuclear@0 750 // POLYFACE vertex indices
nuclear@0 751 case 71:
nuclear@0 752 case 72:
nuclear@0 753 case 73:
nuclear@0 754 case 74:
nuclear@0 755 if (cnti == 4) {
nuclear@0 756 DefaultLogger::get()->warn("DXF: more than 4 indices per face not supported; ignoring");
nuclear@0 757 break;
nuclear@0 758 }
nuclear@0 759 indices[cnti++] = reader.ValueAsUnsignedInt();
nuclear@0 760 break;
nuclear@0 761
nuclear@0 762 // color
nuclear@0 763 case 62:
nuclear@0 764 clr = g_aclrDxfIndexColors[reader.ValueAsUnsignedInt() % AI_DXF_NUM_INDEX_COLORS];
nuclear@0 765 break;
nuclear@0 766 };
nuclear@0 767
nuclear@0 768 reader++;
nuclear@0 769 }
nuclear@0 770
nuclear@0 771 if (line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH && !(flags & DXF_VERTEX_FLAG_PART_OF_POLYFACE)) {
nuclear@0 772 DefaultLogger::get()->warn("DXF: expected vertex to be part of a polyface but the 0x128 flag isn't set");
nuclear@0 773 }
nuclear@0 774
nuclear@0 775 if (cnti) {
nuclear@0 776 line.counts.push_back(cnti);
nuclear@0 777 for (unsigned int i = 0; i < cnti; ++i) {
nuclear@0 778 // IMPORTANT NOTE: POLYMESH indices are ONE-BASED
nuclear@0 779 if (indices[i] == 0) {
nuclear@0 780 DefaultLogger::get()->warn("DXF: invalid vertex index, indices are one-based.");
nuclear@0 781 --line.counts.back();
nuclear@0 782 continue;
nuclear@0 783 }
nuclear@0 784 line.indices.push_back(indices[i]-1);
nuclear@0 785 }
nuclear@0 786 }
nuclear@0 787 else {
nuclear@0 788 line.positions.push_back(out);
nuclear@0 789 line.colors.push_back(clr);
nuclear@0 790 }
nuclear@0 791 }
nuclear@0 792
nuclear@0 793 // ------------------------------------------------------------------------------------------------
nuclear@0 794 void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output)
nuclear@0 795 {
nuclear@0 796 // (note) this is also used for for parsing line entities, so we
nuclear@0 797 // must handle the vertex_count == 2 case as well.
nuclear@0 798
nuclear@0 799 output.blocks.back().lines.push_back( boost::shared_ptr<DXF::PolyLine>( new DXF::PolyLine() ) );
nuclear@0 800 DXF::PolyLine& line = *output.blocks.back().lines.back();
nuclear@0 801
nuclear@0 802 aiVector3D vip[4];
nuclear@0 803 aiColor4D clr = AI_DXF_DEFAULT_COLOR;
nuclear@0 804
nuclear@0 805 bool b[4] = {false,false,false,false};
nuclear@0 806 while( !reader.End() ) {
nuclear@0 807
nuclear@0 808 // next entity with a groupcode == 0 is probably already the next vertex or polymesh entity
nuclear@0 809 if (reader.GroupCode() == 0) {
nuclear@0 810 break;
nuclear@0 811 }
nuclear@0 812 switch (reader.GroupCode())
nuclear@0 813 {
nuclear@0 814
nuclear@0 815 // 8 specifies the layer
nuclear@0 816 case 8:
nuclear@0 817 line.layer = reader.Value();
nuclear@0 818 break;
nuclear@0 819
nuclear@0 820 // x position of the first corner
nuclear@0 821 case 10: vip[0].x = reader.ValueAsFloat();
nuclear@0 822 b[2] = true;
nuclear@0 823 break;
nuclear@0 824
nuclear@0 825 // y position of the first corner
nuclear@0 826 case 20: vip[0].y = reader.ValueAsFloat();
nuclear@0 827 b[2] = true;
nuclear@0 828 break;
nuclear@0 829
nuclear@0 830 // z position of the first corner
nuclear@0 831 case 30: vip[0].z = reader.ValueAsFloat();
nuclear@0 832 b[2] = true;
nuclear@0 833 break;
nuclear@0 834
nuclear@0 835 // x position of the second corner
nuclear@0 836 case 11: vip[1].x = reader.ValueAsFloat();
nuclear@0 837 b[3] = true;
nuclear@0 838 break;
nuclear@0 839
nuclear@0 840 // y position of the second corner
nuclear@0 841 case 21: vip[1].y = reader.ValueAsFloat();
nuclear@0 842 b[3] = true;
nuclear@0 843 break;
nuclear@0 844
nuclear@0 845 // z position of the second corner
nuclear@0 846 case 31: vip[1].z = reader.ValueAsFloat();
nuclear@0 847 b[3] = true;
nuclear@0 848 break;
nuclear@0 849
nuclear@0 850 // x position of the third corner
nuclear@0 851 case 12: vip[2].x = reader.ValueAsFloat();
nuclear@0 852 b[0] = true;
nuclear@0 853 break;
nuclear@0 854
nuclear@0 855 // y position of the third corner
nuclear@0 856 case 22: vip[2].y = reader.ValueAsFloat();
nuclear@0 857 b[0] = true;
nuclear@0 858 break;
nuclear@0 859
nuclear@0 860 // z position of the third corner
nuclear@0 861 case 32: vip[2].z = reader.ValueAsFloat();
nuclear@0 862 b[0] = true;
nuclear@0 863 break;
nuclear@0 864
nuclear@0 865 // x position of the fourth corner
nuclear@0 866 case 13: vip[3].x = reader.ValueAsFloat();
nuclear@0 867 b[1] = true;
nuclear@0 868 break;
nuclear@0 869
nuclear@0 870 // y position of the fourth corner
nuclear@0 871 case 23: vip[3].y = reader.ValueAsFloat();
nuclear@0 872 b[1] = true;
nuclear@0 873 break;
nuclear@0 874
nuclear@0 875 // z position of the fourth corner
nuclear@0 876 case 33: vip[3].z = reader.ValueAsFloat();
nuclear@0 877 b[1] = true;
nuclear@0 878 break;
nuclear@0 879
nuclear@0 880 // color
nuclear@0 881 case 62:
nuclear@0 882 clr = g_aclrDxfIndexColors[reader.ValueAsUnsignedInt() % AI_DXF_NUM_INDEX_COLORS];
nuclear@0 883 break;
nuclear@0 884 };
nuclear@0 885
nuclear@0 886 ++reader;
nuclear@0 887 }
nuclear@0 888
nuclear@0 889 // the fourth corner may even be identical to the third,
nuclear@0 890 // in this case we treat it as if it didn't exist.
nuclear@0 891 if (vip[3] == vip[2]) {
nuclear@0 892 b[1] = false;
nuclear@0 893 }
nuclear@0 894
nuclear@0 895 // sanity checks to see if we got something meaningful
nuclear@0 896 if ((b[1] && !b[0]) || !b[2] || !b[3]) {
nuclear@0 897 DefaultLogger::get()->warn("DXF: unexpected vertex setup in 3DFACE/LINE/FACE entity; ignoring");
nuclear@0 898 output.blocks.back().lines.pop_back();
nuclear@0 899 return;
nuclear@0 900 }
nuclear@0 901
nuclear@0 902 const unsigned int cnt = (2+(b[0]?1:0)+(b[1]?1:0));
nuclear@0 903 line.counts.push_back(cnt);
nuclear@0 904
nuclear@0 905 for (unsigned int i = 0; i < cnt; ++i) {
nuclear@0 906 line.indices.push_back(line.positions.size());
nuclear@0 907 line.positions.push_back(vip[i]);
nuclear@0 908 line.colors.push_back(clr);
nuclear@0 909 }
nuclear@0 910 }
nuclear@0 911
nuclear@0 912 #endif // !! ASSIMP_BUILD_NO_DXF_IMPORTER
nuclear@0 913