1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF 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 | |
62 | using 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. |
73 | static 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 | |
96 | static 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 |
111 | DXFImporter::DXFImporter() |
112 | {} |
113 | |
114 | // ------------------------------------------------------------------------------------------------ |
115 | // Destructor, private as well |
116 | DXFImporter::~DXFImporter() |
117 | {} |
118 | |
119 | // ------------------------------------------------------------------------------------------------ |
120 | // Returns whether the class can handle the format of the given file. |
121 | bool 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 |
128 | const aiImporterDesc* DXFImporter::GetInfo () const |
129 | { |
130 | return &desc; |
131 | } |
132 | |
133 | // ------------------------------------------------------------------------------------------------ |
134 | // Imports the given file into the given scene structure. |
135 | void 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 | // ------------------------------------------------------------------------------------------------ |
219 | void 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 | // ------------------------------------------------------------------------------------------------ |
361 | void 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 | // ------------------------------------------------------------------------------------------------ |
405 | void 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 | // ------------------------------------------------------------------------------------------------ |
432 | void 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 | // ------------------------------------------------------------------------------------------------ |
458 | void DXFImporter::SkipSection(DXF::LineReader& reader) |
459 | { |
460 | for( ;!reader.End() && !reader.Is(0,"ENDSEC" ); reader++); |
461 | } |
462 | |
463 | |
464 | // ------------------------------------------------------------------------------------------------ |
465 | void DXFImporter::(DXF::LineReader& reader, DXF::FileData& /*output*/) |
466 | { |
467 | for( ;!reader.End() && !reader.Is(0,"ENDSEC" ); reader++); |
468 | } |
469 | |
470 | |
471 | // ------------------------------------------------------------------------------------------------ |
472 | void 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 | // ------------------------------------------------------------------------------------------------ |
489 | void 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 | // ------------------------------------------------------------------------------------------------ |
536 | void 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 | |
570 | void 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 | // ------------------------------------------------------------------------------------------------ |
621 | void 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 | // ------------------------------------------------------------------------------------------------ |
722 | void 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 | // ------------------------------------------------------------------------------------------------ |
800 | void 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 | |