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