KiCad PCB EDA Suite
Loading...
Searching...
No Matches
loadmodel.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2016 Cirilo Bernardo <[email protected]>
5 * Copyright (C) 2020-2024 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 *
24 *
25 * Some code lifted from FreeCAD, copyright (c) 2018 Zheng, Lei (realthunder) under GPLv2
26 */
27
28#include <iostream>
29#include <fstream>
30#include <sstream>
31#include <string>
32#include <cstring>
33#include <map>
34#include <vector>
35#include <wx/filename.h>
36#include <wx/log.h>
37#include <wx/stdpaths.h>
38#include <wx/string.h>
39#include <wx/utils.h>
40#include <wx/wfstream.h>
41#include <wx/zipstrm.h>
42
43#include <advanced_config.h>
44#include <decompress.hpp>
45
46#include <TDocStd_Document.hxx>
47#include <TopoDS.hxx>
48#include <TopoDS_Shape.hxx>
49#include <Quantity_Color.hxx>
50#include <XCAFApp_Application.hxx>
51
52#include <AIS_Shape.hxx>
53
54#include <IGESControl_Reader.hxx>
55#include <IGESCAFControl_Reader.hxx>
56#include <Interface_Static.hxx>
57
58#include <STEPControl_Reader.hxx>
59#include <STEPCAFControl_Reader.hxx>
60
61#include <XCAFDoc_DocumentTool.hxx>
62#include <XCAFDoc_ColorTool.hxx>
63#include <XCAFDoc_ShapeTool.hxx>
64
65#include <BRep_Tool.hxx>
66#include <BRepMesh_IncrementalMesh.hxx>
67
68#include <TopoDS.hxx>
69#include <TopoDS_Shape.hxx>
70#include <TopoDS_Face.hxx>
71#include <TopoDS_Compound.hxx>
72#include <TopExp_Explorer.hxx>
73
74#include <Quantity_Color.hxx>
75#include <Poly_Triangulation.hxx>
76#include <Poly_PolygonOnTriangulation.hxx>
77#include <Precision.hxx>
78
79#include <TDF_LabelSequence.hxx>
80#include <TDF_ChildIterator.hxx>
81#include <TDF_Tool.hxx>
82#include <TDataStd_Name.hxx>
83#include <Standard_Version.hxx>
84
86
87
88// log mask for wxLogTrace
89#define MASK_OCE wxT( "PLUGIN_OCE" )
90#define MASK_OCE_EXTRA wxT( "PLUGIN_OCE_EXTRA" )
91
92typedef std::map<std::size_t, SGNODE*> COLORMAP;
93typedef std::map<std::string, SGNODE*> FACEMAP;
94typedef std::map<std::string, std::vector<SGNODE*>> NODEMAP;
95typedef std::pair<std::string, std::vector<SGNODE*>> NODEITEM;
96
97struct DATA;
98
99bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
100 std::vector< SGNODE* >* aItems );
101
102
103bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent,
104 std::vector< SGNODE* >* items, Quantity_ColorRGBA* color );
105
106#if OCC_VERSION_HEX >= 0x070500
107#define OCC_COLOR_SPACE Quantity_TOC_sRGB
108#else
109#define OCC_COLOR_SPACE Quantity_TOC_RGB
110#endif
111
112struct DATA
113{
114 Handle( TDocStd_Document ) m_doc;
115 Handle( XCAFDoc_ColorTool ) m_color;
116 Handle( XCAFDoc_ShapeTool ) m_assy;
119 Quantity_Color refColor;
120 NODEMAP shapes; // SGNODE lists representing a TopoDS_SOLID / COMPOUND
121 COLORMAP colors; // SGAPPEARANCE nodes
122 FACEMAP faces; // SGSHAPE items representing a TopoDS_FACE
123 bool renderBoth; // set TRUE if we're processing IGES
124 bool hasSolid; // set TRUE if there is no parent SOLID
125
127 {
128 scene = nullptr;
129 defaultColor = nullptr;
130 refColor.SetValues( Quantity_NOC_BLACK );
131 renderBoth = false;
132 hasSolid = false;
133 }
134
136 {
137 // destroy any colors with no parent
138 if( !colors.empty() )
139 {
140 COLORMAP::iterator sC = colors.begin();
141 COLORMAP::iterator eC = colors.end();
142
143 while( sC != eC )
144 {
145 if( nullptr == S3D::GetSGNodeParent( sC->second ) )
146 S3D::DestroyNode( sC->second );
147
148 ++sC;
149 }
150
151 colors.clear();
152 }
153
154 if( defaultColor && nullptr == S3D::GetSGNodeParent( defaultColor ) )
156
157 // destroy any faces with no parent
158 if( !faces.empty() )
159 {
160 FACEMAP::iterator sF = faces.begin();
161 FACEMAP::iterator eF = faces.end();
162
163 while( sF != eF )
164 {
165 if( nullptr == S3D::GetSGNodeParent( sF->second ) )
166 S3D::DestroyNode( sF->second );
167
168 ++sF;
169 }
170
171 faces.clear();
172 }
173
174 // destroy any shapes with no parent
175 if( !shapes.empty() )
176 {
177 NODEMAP::iterator sS = shapes.begin();
178 NODEMAP::iterator eS = shapes.end();
179
180 while( sS != eS )
181 {
182 std::vector< SGNODE* >::iterator sV = sS->second.begin();
183 std::vector< SGNODE* >::iterator eV = sS->second.end();
184
185 while( sV != eV )
186 {
187 if( nullptr == S3D::GetSGNodeParent( *sV ) )
188 S3D::DestroyNode( *sV );
189
190 ++sV;
191 }
192
193 sS->second.clear();
194 ++sS;
195 }
196
197 shapes.clear();
198 }
199
200 if( scene )
202 }
203
204 // find collection of tagged nodes
205 bool GetShape( const std::string& id, std::vector< SGNODE* >*& listPtr )
206 {
207 listPtr = nullptr;
208 NODEMAP::iterator item;
209 item = shapes.find( id );
210
211 if( item == shapes.end() )
212 return false;
213
214 listPtr = &item->second;
215 return true;
216 }
217
218 // find collection of tagged nodes
219 SGNODE* GetFace( const std::string& id )
220 {
221 FACEMAP::iterator item;
222 item = faces.find( id );
223
224 if( item == faces.end() )
225 return nullptr;
226
227 return item->second;
228 }
229
230 // return color if found; if not found, create SGAPPEARANCE
231 SGNODE* GetColor( Quantity_ColorRGBA* colorObj )
232 {
233 if( nullptr == colorObj )
234 {
235 if( defaultColor )
236 return defaultColor;
237
238 IFSG_APPEARANCE app( true );
239 app.SetShininess( 0.05f );
240 app.SetSpecular( 0.04f, 0.04f, 0.04f );
241 app.SetAmbient( 0.1f, 0.1f, 0.1f );
242 app.SetDiffuse( 0.6f, 0.6f, 0.6f );
243
244 defaultColor = app.GetRawPtr();
245 return defaultColor;
246 }
247
248 Quantity_Color colorRgb = colorObj->GetRGB();
249
250 Standard_Real r, g, b;
251 colorObj->GetRGB().Values( r, g, b, OCC_COLOR_SPACE );
252
253 std::size_t hash = std::hash<double>{}( colorRgb.Distance( refColor ) )
254 ^ ( std::hash<float>{}( colorObj->Alpha() ) << 1 );
255
256 std::map<std::size_t, SGNODE*>::iterator item;
257 item = colors.find( hash );
258
259 if( item != colors.end() )
260 return item->second;
261
262 IFSG_APPEARANCE app( true );
263 app.SetShininess( 0.1f );
264 app.SetSpecular( 0.12f, 0.12f, 0.12f );
265 app.SetAmbient( 0.1f, 0.1f, 0.1f );
266 app.SetDiffuse( r, g, b );
267 app.SetTransparency( 1.0f - colorObj->Alpha() );
268 colors.emplace( hash, app.GetRawPtr() );
269
270 return app.GetRawPtr();
271 }
272};
273
274
276{
282
283
284FormatType fileType( const char* aFileName )
285{
286 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
287 wxFFileInputStream ifile( fname.GetFullPath() );
288
289 if( !ifile.IsOk() )
290 return FMT_NONE;
291
292 if( fname.GetExt().MakeUpper().EndsWith( wxT( "STPZ" ) )
293 || fname.GetExt().MakeUpper().EndsWith( wxT( "GZ" ) ) )
294 {
295 return FMT_STPZ;
296 }
297
298 char iline[82];
299
300 // The expected header should be the first line.
301 // However some files can have a comment at the beginning of the file
302 // So read up to max_line_count lines to try to find the actual header
303 const int max_line_count = 3;
304
305 for( int ii = 0; ii < max_line_count; ii++ )
306 {
307 memset( iline, 0, 82 );
308 ifile.Read( iline, 82 );
309 iline[81] = 0; // ensure NULL termination when string is too long
310
311 // check for STEP in Part 21 format
312 // (this can give false positives since Part 21 is not exclusively STEP)
313 if( !strncmp( iline, "ISO-10303-21;", 13 ) )
314 {
315 return FMT_STEP;
316 break;
317 }
318
319 std::string fstr = iline;
320
321 // check for STEP in XML format
322 // (this can give both false positive and false negatives)
323 if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos )
324 return FMT_STEP;
325
326 // Note: this is a very simple test which can yield false positives; the only
327 // sure method for determining if a file *not* an IGES model is to attempt
328 // to load it.
329 if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
330 return FMT_IGES;
331
332 // Only a comment (starting by "/*") is allowed as header
333 if( strncmp( iline, "/*", 2 ) != 0 ) // not a comment
334 break;
335 }
336
337 return FMT_NONE;
338}
339
340
347void getTag( const TDF_Label& aLabel, std::string& aTag )
348{
349 std::ostringstream ostr;
350
351 if( aLabel.IsNull() )
352 {
353 wxLogTrace( MASK_OCE, wxT( "Null label passed to getTag" ) );
354 return;
355 }
356
357 TColStd_ListOfInteger tagList;
358 TDF_Tool::TagList( aLabel, tagList );
359
360 for( TColStd_ListOfInteger::Iterator it( tagList ); it.More(); it.Next() )
361 {
362 ostr << it.Value();
363 ostr << ":";
364 }
365
366 aTag = ostr.str();
367 aTag.pop_back(); // kill the last colon
368}
369
370
371static wxString getLabelName( const TDF_Label& aLabel )
372{
373 wxString txt;
374 Handle( TDataStd_Name ) name;
375 if( !aLabel.IsNull() && aLabel.FindAttribute( TDataStd_Name::GetID(), name ) )
376 {
377 TCollection_ExtendedString extstr = name->Get();
378 char* str = new char[extstr.LengthOfCString() + 1];
379 extstr.ToUTF8CString( str );
380
381 txt = wxString::FromUTF8( str );
382 delete[] str;
383 txt = txt.Trim();
384 }
385 return txt;
386}
387
388
394std::string getShapeName( TopAbs_ShapeEnum aShape )
395{
396 switch( aShape )
397 {
398 case TopAbs_COMPOUND: return "COMPOUND";
399 case TopAbs_COMPSOLID: return "COMPSOLID";
400 case TopAbs_SOLID: return "SOLID";
401 case TopAbs_SHELL: return "SHELL";
402 case TopAbs_FACE: return "FACE";
403 case TopAbs_WIRE: return "WIRE";
404 case TopAbs_EDGE: return "EDGE";
405 case TopAbs_VERTEX: return "VERTEX";
406 case TopAbs_SHAPE: return "SHAPE";
407 }
408
409 return "UNKNOWN";
410}
411
412static int colorFloatToDecimal( float aVal )
413{
414 return aVal * 255;
415}
416
417
418static inline std::ostream& operator<<( std::ostream& aOStream, const Quantity_ColorRGBA& aColor )
419{
420 Quantity_Color rgb = aColor.GetRGB();
421
422 return aOStream << "rgba(" << colorFloatToDecimal( rgb.Red() ) << ","
423 << colorFloatToDecimal( rgb.Green() ) << ","
424 << colorFloatToDecimal( rgb.Blue() ) << ","
425 << colorFloatToDecimal( aColor.Alpha() )
426 << ")";
427}
428
429
438static void printLabel( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
439 Handle( XCAFDoc_ColorTool ) aColorTool, const char* aPreMsg = nullptr )
440{
441 if( aLabel.IsNull() )
442 return;
443
444 if( !aPreMsg )
445 aPreMsg = "Label: ";
446
447 TCollection_AsciiString entry;
448 TDF_Tool::Entry( aLabel, entry );
449 std::ostringstream ss;
450 ss << aPreMsg << entry << ", " << getLabelName( aLabel )
451 << ( aShapeTool->IsShape( aLabel ) ? ", shape" : "" )
452 << ( aShapeTool->IsTopLevel( aLabel ) ? ", topLevel" : "" )
453 << ( aShapeTool->IsFree( aLabel ) ? ", free" : "" )
454 << ( aShapeTool->IsAssembly( aLabel ) ? ", assembly" : "" )
455 << ( aShapeTool->IsSimpleShape( aLabel ) ? ", simple" : "" )
456 << ( aShapeTool->IsCompound( aLabel ) ? ", compound" : "" )
457 << ( aShapeTool->IsReference( aLabel ) ? ", reference" : "" )
458 << ( aShapeTool->IsComponent( aLabel ) ? ", component" : "" )
459 << ( aShapeTool->IsSubShape( aLabel ) ? ", subshape" : "" );
460
461 if( aShapeTool->IsSubShape( aLabel ) )
462 {
463 auto shape = aShapeTool->GetShape( aLabel );
464 if( !shape.IsNull() )
465 ss << ", " << getShapeName( shape.ShapeType() );
466 }
467
468 if( aShapeTool->IsShape( aLabel ) )
469 {
470 Quantity_ColorRGBA c;
471 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorGen, c ) )
472 ss << ", gc: " << c;
473 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorSurf, c ) )
474 ss << ", sc: " << c;
475 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorCurv, c ) )
476 ss << ", cc: " << c;
477 }
478
479 wxLogTrace( MASK_OCE, ss.str().c_str() );
480}
481
482
491static void dumpLabels( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
492 Handle( XCAFDoc_ColorTool ) aColorTool, int aDepth = 0 )
493{
494 std::string indent( aDepth * 2, ' ' );
495 printLabel( aLabel, aShapeTool, aColorTool, indent.c_str() );
496 TDF_ChildIterator it;
497 for( it.Initialize( aLabel ); it.More(); it.Next() )
498 dumpLabels( it.Value(), aShapeTool, aColorTool, aDepth + 1 );
499}
500
501
502bool getColor( DATA& data, TDF_Label label, Quantity_ColorRGBA& color )
503{
504 while( true )
505 {
506 if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) )
507 return true;
508 else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) )
509 return true;
510 else if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) )
511 return true;
512
513 label = label.Father();
514
515 if( label.IsNull() )
516 break;
517 };
518
519 return false;
520}
521
522
523void addItems( SGNODE* parent, std::vector< SGNODE* >* lp )
524{
525 if( nullptr == lp )
526 return;
527
528 std::vector< SGNODE* >::iterator sL = lp->begin();
529 std::vector< SGNODE* >::iterator eL = lp->end();
530 SGNODE* item;
531
532 while( sL != eL )
533 {
534 item = *sL;
535
536 if( nullptr == S3D::GetSGNodeParent( item ) )
537 S3D::AddSGNodeChild( parent, item );
538 else
539 S3D::AddSGNodeRef( parent, item );
540
541 ++sL;
542 }
543}
544
545
546bool readIGES( Handle( TDocStd_Document ) & m_doc, const char* fname )
547{
548 IGESCAFControl_Reader reader;
549 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
550 reader.PrintCheckLoad( Standard_False, IFSelect_ItemsByEntity );
551
552 if( stat != IFSelect_RetDone )
553 return false;
554
555 // Enable file-defined shape precision
556 if( !Interface_Static::SetIVal( "read.precision.mode", 0 ) )
557 return false;
558
559 // set other translation options
560 reader.SetColorMode(true); // use model colors
561 reader.SetNameMode(false); // don't use IGES label names
562 reader.SetLayerMode(false); // ignore LAYER data
563
564 if( !reader.Transfer( m_doc ) )
565 {
566 if( m_doc->CanClose() == CDM_CCS_OK )
567 m_doc->Close();
568
569 return false;
570 }
571
572 // are there any shapes to translate?
573 if( reader.NbShapes() < 1 )
574 {
575 if( m_doc->CanClose() == CDM_CCS_OK )
576 m_doc->Close();
577
578 return false;
579 }
580
581 return true;
582}
583
584
585bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname )
586{
587 wxLogTrace( MASK_OCE, wxT( "Reading step file %s" ), fname );
588
589 STEPCAFControl_Reader reader;
590 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
591
592 if( stat != IFSelect_RetDone )
593 return false;
594
595 // Enable user-defined shape precision
596 if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
597 return false;
598
599 // Set the shape conversion precision (default 0.0001 has too many triangles)
600 if( !Interface_Static::SetRVal( "read.precision.val", ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection ) )
601 return false;
602
603 // set other translation options
604 reader.SetColorMode( true ); // use model colors
605 reader.SetNameMode( false ); // don't use label names
606 reader.SetLayerMode( false ); // ignore LAYER data
607
608 if( !reader.Transfer( m_doc ) )
609 {
610 if( m_doc->CanClose() == CDM_CCS_OK )
611 m_doc->Close();
612
613 return false;
614 }
615
616 // are there any shapes to translate?
617 if( reader.NbRootsForTransfer() < 1 )
618 {
619 if( m_doc->CanClose() == CDM_CCS_OK )
620 m_doc->Close();
621
622 return false;
623 }
624
625 return true;
626}
627
628
629bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName )
630{
631 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
632 wxFFileInputStream ifile( fname.GetFullPath() );
633
634 wxFileName outFile( fname );
635
636 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
637 outFile.SetExt( wxT( "STEP" ) );
638
639 wxFileOffset size = ifile.GetLength();
640 wxBusyCursor busycursor;
641
642 if( size == wxInvalidOffset )
643 return false;
644
645 {
646 bool success = false;
647 wxFFileOutputStream ofile( outFile.GetFullPath() );
648
649 if( !ofile.IsOk() )
650 return false;
651
652 char *buffer = new char[size];
653
654 ifile.Read( buffer, size);
655 std::string expanded;
656
657 try
658 {
659 expanded = gzip::decompress( buffer, size );
660 success = true;
661 }
662 catch(...)
663 {}
664
665 if( expanded.empty() )
666 {
667 ifile.Reset();
668 ifile.SeekI( 0 );
669 wxZipInputStream izipfile( ifile );
670 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
671
672 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
673 {
674 izipfile.Read( ofile );
675 success = true;
676 }
677 }
678 else
679 {
680 ofile.Write( expanded.data(), expanded.size() );
681 }
682
683 delete[] buffer;
684 ofile.Close();
685
686 if( !success )
687 return false;
688 }
689
690 bool retval = readSTEP( m_doc, outFile.GetFullPath().mb_str() );
691
692 // Cleanup our temporary file
693 wxRemoveFile( outFile.GetFullPath() );
694
695 return retval;
696}
697
698
699SCENEGRAPH* LoadModel( char const* filename )
700{
701 DATA data;
702
703 Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
704 m_app->NewDocument( "MDTV-XCAF", data.m_doc );
705 FormatType modelFmt = fileType( filename );
706
707 switch( modelFmt )
708 {
709 case FMT_IGES:
710 data.renderBoth = true;
711
712 if( !readIGES( data.m_doc, filename ) )
713 return nullptr;
714
715 break;
716
717 case FMT_STEP:
718 if( !readSTEP( data.m_doc, filename ) )
719 return nullptr;
720
721 break;
722
723 case FMT_STPZ:
724 if( !readSTEPZ( data.m_doc, filename ) )
725 return nullptr;
726
727 break;
728
729
730 default:
731 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
732 m_app->Close( data.m_doc );
733
734 return nullptr;
735 break;
736 }
737
738 data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() );
739 data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() );
740
741 // Check if the log mask is enabled otherwise the dump routine may be expensive before the wxLog call
742 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
743 {
744 dumpLabels( data.m_doc->Main(), data.m_assy, data.m_color );
745 }
746
747 // retrieve all free shapes
748 TDF_LabelSequence frshapes;
749 data.m_assy->GetFreeShapes( frshapes );
750
751 bool ret = false;
752
753 // create the top level SG node
754 IFSG_TRANSFORM topNode( true );
755 data.scene = topNode.GetRawPtr();
756
757 for( Standard_Integer i = 1; i <= frshapes.Length(); i++ )
758 {
759 const TDF_Label& label = frshapes.Value( i );
760
761 if( data.m_color->IsVisible( label ) )
762 {
763 if( processLabel( label, data, data.scene, nullptr ) )
764 ret = true;
765 }
766 }
767
768 if( !ret )
769 {
770 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
771 m_app->Close( data.m_doc );
772
773 return nullptr;
774 }
775
776 SCENEGRAPH* scene = (SCENEGRAPH*)data.scene;
777
778 // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
779#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
780 if( data.scene )
781 {
782 wxFileName fn( wxString::FromUTF8Unchecked( filename ) );
783 wxString output;
784
785 if( FMT_STEP == modelFmt )
786 output = wxT( "_step-" );
787 else
788 output = wxT( "_iges-" );
789
790 output.append( fn.GetName() );
791 output.append( wxT( ".wrl" ) );
792 S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true );
793 }
794#endif
795
796 // set to NULL to prevent automatic destruction of the scene data
797 data.scene = nullptr;
798
799 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
800 m_app->Close( data.m_doc );
801
802 return scene;
803}
804
805
806bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
807 std::vector<SGNODE*>* items, Quantity_ColorRGBA* color )
808{
809 TopoDS_Iterator it;
810 bool ret = false;
811
812 wxLogTrace( MASK_OCE, wxT( "Processing shell" ) );
813 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
814 {
815 const TopoDS_Face& face = TopoDS::Face( it.Value() );
816
817 if( processFace( face, data, parent, items, color ) )
818 ret = true;
819 }
820
821 return ret;
822}
823
824
825bool processSolidOrShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
826 std::vector< SGNODE* >* items )
827{
828 TDF_Label label;
829 std::string partID;
830 Quantity_ColorRGBA col;
831 Quantity_ColorRGBA* lcolor = nullptr;
832
833 data.hasSolid = shape.ShapeType() == TopAbs_SOLID;
834
835 wxLogTrace( MASK_OCE, wxT( "Processing solid" ) );
836
837 // Search the whole model first to make sure something exists (may or may not have color)
838 if( !data.m_assy->Search( shape, label ) )
839 {
840 static int i = 0;
841 std::ostringstream ostr;
842 ostr << "KMISC_" << i++;
843 partID = ostr.str();
844 }
845 else
846 {
847 bool found_color = false;
848
849 if( getColor( data, label, col ) )
850 {
851 found_color = true;
852 lcolor = &col;
853 }
854
855 // If the top-level label doesn't have the color information, search components
856 if( !found_color )
857 {
858 if( data.m_assy->Search( shape, label, Standard_False, Standard_True, Standard_True ) &&
859 getColor( data, label, col ) )
860 {
861 found_color = true;
862 lcolor = &col;
863 }
864 }
865
866 // If the components do not have color information, search all components without location
867 if( !found_color )
868 {
869 if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
870 Standard_True ) &&
871 getColor( data, label, col ) )
872 {
873 found_color = true;
874 lcolor = &col;
875 }
876 }
877
878 // Our last chance to find the color looks for color as a subshape of top-level simple
879 // shapes.
880 if( !found_color )
881 {
882 if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
883 Standard_False ) &&
884 getColor( data, label, col ) )
885 {
886 found_color = true;
887 lcolor = &col;
888 }
889 }
890
891 getTag( label, partID );
892 }
893
894 TopoDS_Iterator it;
895 IFSG_TRANSFORM childNode( parent );
896 SGNODE* pptr = childNode.GetRawPtr();
897 bool ret = false;
898
899
900 std::vector< SGNODE* >* component = nullptr;
901
902 if( !partID.empty() )
903 data.GetShape( partID, component );
904
905 if( component )
906 {
907 addItems( pptr, component );
908
909 if( nullptr != items )
910 items->push_back( pptr );
911 }
912
913 // instantiate resulting object
914 std::vector<SGNODE*> itemList;
915
916 TopAbs_ShapeEnum stype = shape.ShapeType();
917 if( stype == TopAbs_SHELL )
918 {
919 if( processShell( shape, data, pptr, &itemList, lcolor ) )
920 ret = true;
921 }
922 else
923 {
924
925 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
926 {
927 const TopoDS_Shape& subShape = it.Value();
928
929 if( subShape.ShapeType() == TopAbs_SHELL )
930 {
931 if( processShell( subShape, data, pptr, &itemList, lcolor ) )
932 ret = true;
933 }
934 else
935 {
936 wxLogTrace( MASK_OCE, wxT( "Unsupported subshape in solid" ) );
937 }
938 }
939 }
940
941 if( !ret )
942 childNode.Destroy();
943 else if( nullptr != items )
944 items->push_back( pptr );
945
946 return ret;
947}
948
949
950bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
951 std::vector<SGNODE*>* aItems )
952{
953 std::string labelTag;
954
955 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
956 {
957 // can be expensive, guard it if we aren't logging
958 getTag( aLabel, labelTag );
959 }
960
961 wxLogTrace( MASK_OCE, wxT( "Processing label %s" ), labelTag );
962
963 TopoDS_Shape originalShape;
964 TDF_Label shapeLabel = aLabel;
965
966 if( !aData.m_assy->GetShape( shapeLabel, originalShape ) )
967 {
968 return false;
969 }
970
971 TopoDS_Shape shape = originalShape;
972
973 if( aData.m_assy->IsReference( aLabel ) )
974 {
975 wxLogTrace( MASK_OCE, wxT( "Label %s is ref, trying to pull up referred label" ),
976 labelTag );
977
978 if( !aData.m_assy->GetReferredShape( aLabel, shapeLabel ) )
979 {
980 return false;
981 }
982
983 labelTag = static_cast<int>( shapeLabel.Tag() );
984 // wxLogTrace( MASK_OCE, wxT( "Label %s referred" ), labelTag );
985
986 if( !aData.m_assy->GetShape( shapeLabel, shape ) )
987 {
988 return false;
989 }
990 }
991
992 // Now let's see if the original label has a location
993 // Labels can be used to place copies of other labels at a specific location
994 IFSG_TRANSFORM childNode( aParent );
995 SGNODE* pptr = childNode.GetRawPtr();
996 const TopLoc_Location& loc = originalShape.Location();
997
998 if( !loc.IsIdentity() )
999 {
1000 wxLogTrace( MASK_OCE, wxT( "Label %d has location" ), static_cast<int>( aLabel.Tag() ) );
1001 gp_Trsf T = loc.Transformation();
1002 gp_XYZ coord = T.TranslationPart();
1003 childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
1004 wxLogTrace( MASK_OCE, wxT( "Translation %f, %f, %f" ), coord.X(), coord.Y(), coord.Z() );
1005 gp_XYZ axis;
1006 Standard_Real angle;
1007
1008 if( T.GetRotation( axis, angle ) )
1009 {
1010 childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
1011 wxLogTrace( MASK_OCE, wxT( "Rotation %f, %f, %f, angle %f" ),
1012 axis.X(), axis.Y(), axis.Z(), angle );
1013 }
1014 }
1015
1016 TopAbs_ShapeEnum stype = shape.ShapeType();
1017 bool ret = false;
1018 aData.hasSolid = false;
1019
1020 switch( stype )
1021 {
1022 case TopAbs_COMPOUND:
1023 {
1024 // assemblies will report a shape type of compound which isn't what we are after
1025 // we will still process the children of assemblies but they should just be label references to the actual shapes
1026 if( !aData.m_assy->IsAssembly( shapeLabel ) )
1027 {
1028 TopExp_Explorer xp;
1029
1030 for( xp.Init( shape, TopAbs_SOLID ); xp.More(); xp.Next() )
1031 {
1032 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1033 ret = true;
1034 }
1035
1036 // Get all shells, avoid those that may be attached to solids
1037 for( xp.Init( shape, TopAbs_SHELL, TopAbs_SOLID ); xp.More(); xp.Next() )
1038 {
1039 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1040 ret = true;
1041 }
1042
1043 // Get all faces, avoid those that may be attached to shells
1044 for( xp.Init( shape, TopAbs_FACE, TopAbs_SHELL ); xp.More(); xp.Next() )
1045 {
1046 const TopoDS_Face& face = TopoDS::Face( xp.Current() );
1047 processFace( face, aData, pptr, aItems, nullptr );
1048 ret = true;
1049 }
1050 }
1051 }
1052 break;
1053
1054 case TopAbs_SOLID:
1055 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1056 ret = true;
1057
1058 break;
1059
1060 case TopAbs_SHELL:
1061 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1062 ret = true;
1063
1064 break;
1065
1066 case TopAbs_FACE:
1067 if( processFace( TopoDS::Face( shape ), aData, pptr, aItems, nullptr ) )
1068 ret = true;
1069
1070 break;
1071
1072 default:
1073 break;
1074 }
1075
1076 if( nullptr != aItems )
1077 aItems->push_back( pptr );
1078
1079 if( !aData.m_assy->IsSimpleShape( shapeLabel ) && shapeLabel.HasChild() )
1080 {
1081 wxLogTrace( MASK_OCE, wxT( "Label %s has children" ), labelTag );
1082 TDF_ChildIterator it;
1083
1084 for( it.Initialize( shapeLabel ); it.More(); it.Next() )
1085 {
1086 if( processLabel( it.Value(), aData, pptr, aItems ) )
1087 ret = true;
1088 }
1089 }
1090
1091 return ret;
1092}
1093
1094
1095bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, std::vector<SGNODE*>* items,
1096 Quantity_ColorRGBA* color )
1097{
1098 if( Standard_True == face.IsNull() )
1099 return false;
1100
1101 bool reverse = ( face.Orientation() == TopAbs_REVERSED );
1102 SGNODE* ashape = nullptr;
1103 std::string partID;
1104 TDF_Label label;
1105
1106 bool useBothSides = false;
1107
1108 // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant
1109 // of a SOLID then hasSolid = false and we must render both sides
1110 if( data.renderBoth || !data.hasSolid )
1111 useBothSides = true;
1112
1113 if( data.m_assy->FindShape( face, label, Standard_False ) )
1114 getTag( label, partID );
1115
1116 if( !partID.empty() )
1117 ashape = data.GetFace( partID );
1118
1119 if( ashape )
1120 {
1121 if( nullptr == S3D::GetSGNodeParent( ashape ) )
1122 S3D::AddSGNodeChild( parent, ashape );
1123 else
1124 S3D::AddSGNodeRef( parent, ashape );
1125
1126 if( nullptr != items )
1127 items->push_back( ashape );
1128
1129 if( useBothSides )
1130 {
1131 std::string id2 = partID;
1132 id2.append( "b" );
1133 SGNODE* shapeB = data.GetFace( id2 );
1134
1135 if( nullptr == S3D::GetSGNodeParent( shapeB ) )
1136 S3D::AddSGNodeChild( parent, shapeB );
1137 else
1138 S3D::AddSGNodeRef( parent, shapeB );
1139
1140 if( nullptr != items )
1141 items->push_back( shapeB );
1142 }
1143
1144 return true;
1145 }
1146
1147 TopLoc_Location loc;
1148 Standard_Boolean isTessellate (Standard_False);
1149 Handle( Poly_Triangulation ) triangulation = BRep_Tool::Triangulation( face, loc );
1150 const double linDeflection = ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection;
1151
1152 if( triangulation.IsNull() || triangulation->Deflection() > linDeflection + Precision::Confusion() )
1153 isTessellate = Standard_True;
1154
1155 if( isTessellate )
1156 {
1157 BRepMesh_IncrementalMesh IM(face, linDeflection, Standard_False,
1158 glm::radians(ADVANCED_CFG::GetCfg().m_OcePluginAngularDeflection) );
1159 triangulation = BRep_Tool::Triangulation( face, loc );
1160 }
1161
1162 if( triangulation.IsNull() == Standard_True )
1163 return false;
1164
1165 Quantity_ColorRGBA lcolor;
1166
1167 // check for a face color; this has precedence over SOLID colors
1168 if( data.m_color->GetColor( face, XCAFDoc_ColorSurf, lcolor )
1169 || data.m_color->GetColor( face, XCAFDoc_ColorCurv, lcolor )
1170 || data.m_color->GetColor( face, XCAFDoc_ColorGen, lcolor ) )
1171 {
1172 color = &lcolor;
1173 }
1174
1175 SGNODE* ocolor = data.GetColor( color );
1176
1177 // create a SHAPE and attach the color and data,
1178 // then attach the shape to the parent and return TRUE
1179 IFSG_SHAPE vshape( true );
1180 IFSG_FACESET vface( vshape );
1181 IFSG_COORDS vcoords( vface );
1182 IFSG_COORDINDEX coordIdx( vface );
1183
1184 if( nullptr == S3D::GetSGNodeParent( ocolor ) )
1185 S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor );
1186 else
1187 S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor );
1188
1189 std::vector< SGPOINT > vertices;
1190 std::vector< int > indices;
1191 std::vector< int > indices2;
1192 gp_Trsf tx;
1193
1194 for( int i = 1; i <= triangulation->NbNodes(); i++ )
1195 {
1196 gp_XYZ v( triangulation->Node(i).Coord() );
1197 vertices.emplace_back( v.X(), v.Y(), v.Z() );
1198 }
1199
1200 for( int i = 1; i <= triangulation->NbTriangles(); i++ )
1201 {
1202 int a, b, c;
1203 triangulation->Triangle(i).Get(a, b, c);
1204 a--;
1205
1206 if( reverse )
1207 {
1208 int tmp = b - 1;
1209 b = c - 1;
1210 c = tmp;
1211 }
1212 else
1213 {
1214 b--;
1215 c--;
1216 }
1217
1218 indices.push_back( a );
1219 indices.push_back( b );
1220 indices.push_back( c );
1221
1222 if( useBothSides )
1223 {
1224 indices2.push_back( b );
1225 indices2.push_back( a );
1226 indices2.push_back( c );
1227 }
1228 }
1229
1230 vcoords.SetCoordsList( vertices.size(), &vertices[0] );
1231 coordIdx.SetIndices( indices.size(), &indices[0] );
1232 vface.CalcNormals( nullptr );
1233 vshape.SetParent( parent );
1234
1235 if( !partID.empty() )
1236 data.faces.emplace( partID, vshape.GetRawPtr() );
1237
1238 // The outer surface of an IGES model is indeterminate so
1239 // we must render both sides of a surface.
1240 if( useBothSides )
1241 {
1242 std::string id2 = partID;
1243 id2.append( "b" );
1244 IFSG_SHAPE vshape2( true );
1245 IFSG_FACESET vface2( vshape2 );
1246 IFSG_COORDS vcoords2( vface2 );
1247 IFSG_COORDINDEX coordIdx2( vface2 );
1248 S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor );
1249
1250 vcoords2.SetCoordsList( vertices.size(), &vertices[0] );
1251 coordIdx2.SetIndices( indices2.size(), &indices2[0] );
1252 vface2.CalcNormals( nullptr );
1253 vshape2.SetParent( parent );
1254
1255 if( !partID.empty() )
1256 data.faces.emplace( id2, vshape2.GetRawPtr() );
1257 }
1258
1259 return true;
1260}
int color
Definition: DXF_plotter.cpp:58
const char * name
Definition: DXF_plotter.cpp:57
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetShininess(float aShininess) noexcept
bool SetAmbient(float aRVal, float aGVal, float aBVal)
IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:41
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:41
bool CalcNormals(SGNODE **aPtr)
bool SetIndices(size_t nIndices, int *aIndexList)
Function SetIndices sets the number of indices and creates a copy of the given index data.
Definition: ifsg_index.cpp:47
bool SetParent(SGNODE *aParent)
Function SetParent sets the parent SGNODE of this object.
Definition: ifsg_node.cpp:87
SGNODE * GetRawPtr(void) noexcept
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:65
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:55
IFSG_SHAPE is the wrapper for the SGSHAPE class.
Definition: ifsg_shape.h:41
IFSG_TRANSFORM is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
bool SetTranslation(const SGPOINT &aTranslation) noexcept
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
Define the basic data set required to represent a 3D model.
Definition: scenegraph.h:45
The base class of all Scene Graph nodes.
Definition: sg_node.h:75
std::ostream & operator<<(std::ostream &aStream, const EDA_TEXT &aText)
Definition: eda_text.cpp:1210
double m_OcePluginLinearDeflection
OCE (STEP/IGES) 3D Plugin Tesselation Linear Deflection.
collects header files for all SG* wrappers and the API
bool processShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
Definition: loadmodel.cpp:806
std::map< std::string, std::vector< SGNODE * > > NODEMAP
Definition: loadmodel.cpp:94
std::map< std::size_t, SGNODE * > COLORMAP
Definition: loadmodel.cpp:92
bool processFace(const TopoDS_Face &face, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
Definition: loadmodel.cpp:1095
std::pair< std::string, std::vector< SGNODE * > > NODEITEM
Definition: loadmodel.cpp:95
bool getColor(DATA &data, TDF_Label label, Quantity_ColorRGBA &color)
Definition: loadmodel.cpp:502
FormatType
Definition: loadmodel.cpp:276
@ FMT_STEP
Definition: loadmodel.cpp:278
@ FMT_IGES
Definition: loadmodel.cpp:280
@ FMT_NONE
Definition: loadmodel.cpp:277
@ FMT_STPZ
Definition: loadmodel.cpp:279
static int colorFloatToDecimal(float aVal)
Definition: loadmodel.cpp:412
#define MASK_OCE
Definition: loadmodel.cpp:89
void getTag(const TDF_Label &aLabel, std::string &aTag)
Gets the absolute tag string for a given label in the form of ##:##:##:##.
Definition: loadmodel.cpp:347
void addItems(SGNODE *parent, std::vector< SGNODE * > *lp)
Definition: loadmodel.cpp:523
std::string getShapeName(TopAbs_ShapeEnum aShape)
Gets a string for a given TopAbs_ShapeEnum element.
Definition: loadmodel.cpp:394
static void printLabel(TDF_Label aLabel, Handle(XCAFDoc_ShapeTool) aShapeTool, Handle(XCAFDoc_ColorTool) aColorTool, const char *aPreMsg=nullptr)
Gets a string for a given TopAbs_ShapeEnum element.
Definition: loadmodel.cpp:438
SCENEGRAPH * LoadModel(char const *filename)
Definition: loadmodel.cpp:699
static void dumpLabels(TDF_Label aLabel, Handle(XCAFDoc_ShapeTool) aShapeTool, Handle(XCAFDoc_ColorTool) aColorTool, int aDepth=0)
Dumps a label and the entire tree underneath it.
Definition: loadmodel.cpp:491
FormatType fileType(const char *aFileName)
Definition: loadmodel.cpp:284
bool readSTEP(Handle(TDocStd_Document)&m_doc, const char *fname)
Definition: loadmodel.cpp:585
#define OCC_COLOR_SPACE
Definition: loadmodel.cpp:109
bool processSolidOrShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
Definition: loadmodel.cpp:825
static wxString getLabelName(const TDF_Label &aLabel)
Definition: loadmodel.cpp:371
bool processLabel(const TDF_Label &aLabel, DATA &aData, SGNODE *aParent, std::vector< SGNODE * > *aItems)
Definition: loadmodel.cpp:950
std::map< std::string, SGNODE * > FACEMAP
Definition: loadmodel.cpp:93
bool readSTEPZ(Handle(TDocStd_Document)&m_doc, const char *aFileName)
Definition: loadmodel.cpp:629
bool readIGES(Handle(TDocStd_Document) &m_doc, const char *fname)
Definition: loadmodel.cpp:546
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:77
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:494
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:149
SGLIB_API bool AddSGNodeChild(SGNODE *aParent, SGNODE *aChild)
Definition: ifsg_api.cpp:512
SGLIB_API bool AddSGNodeRef(SGNODE *aParent, SGNODE *aChild)
Definition: ifsg_api.cpp:503
SGNODE * defaultColor
Definition: loadmodel.cpp:118
COLORMAP colors
Definition: loadmodel.cpp:121
Quantity_Color refColor
Definition: loadmodel.cpp:119
Handle(XCAFDoc_ColorTool) m_color
bool renderBoth
Definition: loadmodel.cpp:123
SGNODE * GetFace(const std::string &id)
Definition: loadmodel.cpp:219
DATA()
Definition: loadmodel.cpp:126
~DATA()
Definition: loadmodel.cpp:135
Handle(TDocStd_Document) m_doc
bool GetShape(const std::string &id, std::vector< SGNODE * > *&listPtr)
Definition: loadmodel.cpp:205
Handle(XCAFDoc_ShapeTool) m_assy
NODEMAP shapes
Definition: loadmodel.cpp:120
SGNODE * scene
Definition: loadmodel.cpp:117
bool hasSolid
Definition: loadmodel.cpp:124
FACEMAP faces
Definition: loadmodel.cpp:122
SGNODE * GetColor(Quantity_ColorRGBA *colorObj)
Definition: loadmodel.cpp:231