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 The 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, see <https://www.gnu.org/licenses/>.
19 *
20 *
21 * Some code lifted from FreeCAD, copyright (c) 2018 Zheng, Lei (realthunder) under GPLv2
22 */
23
24#include <iostream>
25#include <fstream>
26#include <sstream>
27#include <string>
28#include <cstring>
29#include <array>
30#include <map>
31#include <vector>
32#include <atomic>
33#include <wx/filename.h>
34#include <wx/log.h>
35#include <wx/stdpaths.h>
36#include <wx/string.h>
37#include <wx/utils.h>
38#include <wx/wfstream.h>
39#include <wx/zipstrm.h>
40
42#include "loadmodel.h"
44
45#include <TDocStd_Document.hxx>
46#include <TopoDS.hxx>
47#include <TopoDS_Shape.hxx>
48#include <Quantity_Color.hxx>
49#include <Quantity_ColorRGBA.hxx>
50#include <XCAFApp_Application.hxx>
51
52#include <IGESControl_Reader.hxx>
53#include <IGESCAFControl_Reader.hxx>
54#include <Interface_Static.hxx>
55
56#include <STEPControl_Reader.hxx>
57#include <STEPCAFControl_Reader.hxx>
58
59#include <XCAFDoc_DocumentTool.hxx>
60#include <XCAFDoc_ColorTool.hxx>
61#include <XCAFDoc_ShapeTool.hxx>
62
63#include <BRep_Tool.hxx>
64#include <BRepMesh_IncrementalMesh.hxx>
65
66#include <TopoDS.hxx>
67#include <TopoDS_Shape.hxx>
68#include <TopoDS_Face.hxx>
69#include <TopoDS_Compound.hxx>
70#include <TopExp_Explorer.hxx>
71
72#include <Quantity_Color.hxx>
73#include <Poly_Triangulation.hxx>
74#include <Poly_PolygonOnTriangulation.hxx>
75#include <Precision.hxx>
76
77#include <NCollection_Sequence.hxx>
78#include <TDF_Label.hxx>
79#include <TDF_ChildIterator.hxx>
80#include <TDF_Tool.hxx>
81#include <TDataStd_Name.hxx>
82#include <Standard_Version.hxx>
83
85
86
87// log mask for wxLogTrace
88#define MASK_OCE wxT( "PLUGIN_OCE" )
89#define MASK_OCE_EXTRA wxT( "PLUGIN_OCE_EXTRA" )
90
91typedef std::array<double, 4> COLORKEY;
92typedef std::map<COLORKEY, IFSG_APPEARANCE> 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( XCAFApp_Application ) m_app;
115 Handle( TDocStd_Document ) m_doc;
116 Handle( XCAFDoc_ColorTool ) m_color;
117 Handle( XCAFDoc_ShapeTool ) m_assy;
119
122
123 NODEMAP shapes; // SGNODE lists representing a TopoDS_SOLID / COMPOUND
124 COLORMAP colors; // SGAPPEARANCE nodes
125 FACEMAP faces; // SGSHAPE items representing a TopoDS_FACE
126 bool renderBoth; // set TRUE if we're processing IGES
127 bool hasSolid; // set TRUE if there is no parent SOLID
130
132 {
133 if( options.IsCanceled() )
134 {
135 if( status )
136 status->Set( S3D::MODEL_IMPORT_ERROR::CANCELED, "Model import canceled" );
137
138 return true;
139 }
140
141 return false;
142 }
143
145 {
146 scene = nullptr;
147 renderBoth = false;
148 hasSolid = false;
149 }
150
152 {
153 // destroy any colors with no parent
154 if( !colors.empty() )
155 {
156 COLORMAP::iterator sC = colors.begin();
157 COLORMAP::iterator eC = colors.end();
158
159 while( sC != eC )
160 {
161 // A null raw pointer means a shape that owned the node already destroyed it
162 SGNODE* color = sC->second.GetRawPtr();
163
164 if( color && nullptr == S3D::GetSGNodeParent( color ) )
165 S3D::DestroyNode( color );
166
167 ++sC;
168 }
169
170 colors.clear();
171 }
172
173 SGNODE* rawDefaultColor = defaultColor.GetRawPtr();
174
175 if( rawDefaultColor && nullptr == S3D::GetSGNodeParent( rawDefaultColor ) )
176 S3D::DestroyNode( rawDefaultColor );
177
178 // destroy any faces with no parent
179 if( !faces.empty() )
180 {
181 FACEMAP::iterator sF = faces.begin();
182 FACEMAP::iterator eF = faces.end();
183
184 while( sF != eF )
185 {
186 if( nullptr == S3D::GetSGNodeParent( sF->second ) )
187 S3D::DestroyNode( sF->second );
188
189 ++sF;
190 }
191
192 faces.clear();
193 }
194
195 // destroy any shapes with no parent
196 if( !shapes.empty() )
197 {
198 NODEMAP::iterator sS = shapes.begin();
199 NODEMAP::iterator eS = shapes.end();
200
201 while( sS != eS )
202 {
203 std::vector< SGNODE* >::iterator sV = sS->second.begin();
204 std::vector< SGNODE* >::iterator eV = sS->second.end();
205
206 while( sV != eV )
207 {
208 if( nullptr == S3D::GetSGNodeParent( *sV ) )
209 S3D::DestroyNode( *sV );
210
211 ++sV;
212 }
213
214 sS->second.clear();
215 ++sS;
216 }
217
218 shapes.clear();
219 }
220
221 if( scene )
223
224 if( !m_app.IsNull() && !m_doc.IsNull() && m_app->CanClose( m_doc ) == CDM_CCS_OK )
225 m_app->Close( m_doc );
226 }
227
228 // find collection of tagged nodes
229 bool GetShape( const std::string& id, std::vector< SGNODE* >*& listPtr )
230 {
231 listPtr = nullptr;
232 NODEMAP::iterator item;
233 item = shapes.find( id );
234
235 if( item == shapes.end() )
236 return false;
237
238 listPtr = &item->second;
239 return true;
240 }
241
242 // find collection of tagged nodes
243 SGNODE* GetFace( const std::string& id )
244 {
245 FACEMAP::iterator item;
246 item = faces.find( id );
247
248 if( item == faces.end() )
249 return nullptr;
250
251 return item->second;
252 }
253
254 // return color if found; if not found, create SGAPPEARANCE
255 SGNODE* GetColor( Quantity_ColorRGBA* colorObj )
256 {
257 if( nullptr == colorObj )
258 {
259 if( SGNODE* cached = defaultColor.GetRawPtr() )
260 return cached;
261
262 defaultColor.NewNode( nullptr );
263 defaultColor.SetShininess( 0.05f );
264 defaultColor.SetSpecular( 0.04f, 0.04f, 0.04f );
265 defaultColor.SetAmbient( 0.1f, 0.1f, 0.1f );
266 defaultColor.SetDiffuse( 0.6f, 0.6f, 0.6f );
267
268 return defaultColor.GetRawPtr();
269 }
270
271 double r, g, b;
272 colorObj->GetRGB().Values( r, g, b, OCC_COLOR_SPACE );
273
274 COLORKEY key = { r, g, b, colorObj->Alpha() };
275
276 // The wrapper lives in the map so the node can clear it, which turns a cache entry
277 // whose owning shape was destroyed into a miss rather than a dangling pointer
278 IFSG_APPEARANCE& app = colors.try_emplace( key, false ).first->second;
279
280 if( SGNODE* cached = app.GetRawPtr() )
281 return cached;
282
283 app.NewNode( nullptr );
284 app.SetShininess( 0.1f );
285 app.SetSpecular( 0.12f, 0.12f, 0.12f );
286 app.SetAmbient( 0.1f, 0.1f, 0.1f );
287 app.SetDiffuse( r, g, b );
288 app.SetTransparency( 1.0f - colorObj->Alpha() );
289
290 return app.GetRawPtr();
291 }
292};
293
294
302
303
304FormatType fileType( const char* aFileName )
305{
306 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
307 wxFFileInputStream ifile( fname.GetFullPath() );
308
309 if( !ifile.IsOk() )
310 return FMT_NONE;
311
312 wxString extension = fname.GetExt().MakeUpper();
313
314 if( extension == wxT( "STPZ" ) || extension == wxT( "STEPZ" ) || extension.EndsWith( wxT( "GZ" ) ) )
315 {
316 return FMT_STPZ;
317 }
318
319 char iline[82];
320
321 // The expected header should be the first line.
322 // However some files can have a comment at the beginning of the file
323 // So read up to max_line_count lines to try to find the actual header
324 const int max_line_count = 3;
325
326 for( int ii = 0; ii < max_line_count; ii++ )
327 {
328 memset( iline, 0, 82 );
329 ifile.Read( iline, 82 );
330 iline[81] = 0; // ensure NULL termination when string is too long
331
332 // check for STEP in Part 21 format
333 // (this can give false positives since Part 21 is not exclusively STEP)
334 if( !strncmp( iline, "ISO-10303-21;", 13 ) )
335 {
336 return FMT_STEP;
337 break;
338 }
339
340 std::string fstr = iline;
341
342 // check for STEP in XML format
343 // (this can give both false positive and false negatives)
344 if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos )
345 return FMT_STEP;
346
347 // Note: this is a very simple test which can yield false positives; the only
348 // sure method for determining if a file *not* an IGES model is to attempt
349 // to load it.
350 if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
351 return FMT_IGES;
352
353 // Only a comment (starting by "/*") is allowed as header
354 if( strncmp( iline, "/*", 2 ) != 0 ) // not a comment
355 break;
356 }
357
358 return FMT_NONE;
359}
360
361
368void getTag( const TDF_Label& aLabel, std::string& aTag )
369{
370 std::ostringstream ostr;
371
372 if( aLabel.IsNull() )
373 {
374 wxLogTrace( MASK_OCE, wxT( "Null label passed to getTag" ) );
375 return;
376 }
377
378 NCollection_List<int> tagList;
379 TDF_Tool::TagList( aLabel, tagList );
380
381 for( const int& tag : tagList )
382 {
383 ostr << tag;
384 ostr << ":";
385 }
386
387 aTag = ostr.str();
388 aTag.pop_back(); // kill the last colon
389}
390
391
392static wxString getLabelName( const TDF_Label& aLabel )
393{
394 wxString txt;
395 Handle( TDataStd_Name ) name;
396 if( !aLabel.IsNull() && aLabel.FindAttribute( TDataStd_Name::GetID(), name ) )
397 {
398 TCollection_ExtendedString extstr = name->Get();
399 char* str = new char[extstr.LengthOfCString() + 1];
400 extstr.ToUTF8CString( str );
401
402 txt = wxString::FromUTF8( str );
403 delete[] str;
404 txt = txt.Trim();
405 }
406 return txt;
407}
408
409
415std::string getShapeName( TopAbs_ShapeEnum aShape )
416{
417 switch( aShape )
418 {
419 case TopAbs_COMPOUND: return "COMPOUND";
420 case TopAbs_COMPSOLID: return "COMPSOLID";
421 case TopAbs_SOLID: return "SOLID";
422 case TopAbs_SHELL: return "SHELL";
423 case TopAbs_FACE: return "FACE";
424 case TopAbs_WIRE: return "WIRE";
425 case TopAbs_EDGE: return "EDGE";
426 case TopAbs_VERTEX: return "VERTEX";
427 case TopAbs_SHAPE: return "SHAPE";
428 }
429
430 return "UNKNOWN";
431}
432
433static int colorFloatToDecimal( float aVal )
434{
435 return aVal * 255;
436}
437
438
439static inline std::ostream& operator<<( std::ostream& aOStream, const Quantity_ColorRGBA& aColor )
440{
441 Quantity_Color rgb = aColor.GetRGB();
442
443 return aOStream << "rgba(" << colorFloatToDecimal( rgb.Red() ) << ","
444 << colorFloatToDecimal( rgb.Green() ) << ","
445 << colorFloatToDecimal( rgb.Blue() ) << ","
446 << colorFloatToDecimal( aColor.Alpha() )
447 << ")";
448}
449
450
459static void printLabel( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
460 Handle( XCAFDoc_ColorTool ) aColorTool, const char* aPreMsg = nullptr )
461{
462 if( aLabel.IsNull() )
463 return;
464
465 if( !aPreMsg )
466 aPreMsg = "Label: ";
467
468 TCollection_AsciiString entry;
469 TDF_Tool::Entry( aLabel, entry );
470 std::ostringstream ss;
471 ss << aPreMsg << entry << ", " << getLabelName( aLabel )
472 << ( aShapeTool->IsShape( aLabel ) ? ", shape" : "" )
473 << ( aShapeTool->IsTopLevel( aLabel ) ? ", topLevel" : "" )
474 << ( aShapeTool->IsFree( aLabel ) ? ", free" : "" )
475 << ( aShapeTool->IsAssembly( aLabel ) ? ", assembly" : "" )
476 << ( aShapeTool->IsSimpleShape( aLabel ) ? ", simple" : "" )
477 << ( aShapeTool->IsCompound( aLabel ) ? ", compound" : "" )
478 << ( aShapeTool->IsReference( aLabel ) ? ", reference" : "" )
479 << ( aShapeTool->IsComponent( aLabel ) ? ", component" : "" )
480 << ( aShapeTool->IsSubShape( aLabel ) ? ", subshape" : "" );
481
482 if( aShapeTool->IsSubShape( aLabel ) )
483 {
484 auto shape = aShapeTool->GetShape( aLabel );
485 if( !shape.IsNull() )
486 ss << ", " << getShapeName( shape.ShapeType() );
487 }
488
489 if( aShapeTool->IsShape( aLabel ) )
490 {
491 Quantity_ColorRGBA c;
492 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorGen, c ) )
493 ss << ", gc: " << c;
494 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorSurf, c ) )
495 ss << ", sc: " << c;
496 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorCurv, c ) )
497 ss << ", cc: " << c;
498 }
499
500 wxLogTrace( MASK_OCE, ss.str().c_str() );
501}
502
503
512static void dumpLabels( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
513 Handle( XCAFDoc_ColorTool ) aColorTool, int aDepth = 0 )
514{
515 std::string indent( aDepth * 2, ' ' );
516 printLabel( aLabel, aShapeTool, aColorTool, indent.c_str() );
517 TDF_ChildIterator it;
518 for( it.Initialize( aLabel ); it.More(); it.Next() )
519 dumpLabels( it.Value(), aShapeTool, aColorTool, aDepth + 1 );
520}
521
522
523bool getColor( DATA& data, TDF_Label label, Quantity_ColorRGBA& color )
524{
525 while( true )
526 {
527 if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) )
528 return true;
529 else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) )
530 return true;
531 else if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) )
532 return true;
533
534 label = label.Father();
535
536 if( label.IsNull() )
537 break;
538 };
539
540 return false;
541}
542
543
544void addItems( SGNODE* parent, std::vector< SGNODE* >* lp )
545{
546 if( nullptr == lp )
547 return;
548
549 std::vector< SGNODE* >::iterator sL = lp->begin();
550 std::vector< SGNODE* >::iterator eL = lp->end();
551 SGNODE* item;
552
553 while( sL != eL )
554 {
555 item = *sL;
556
557 if( nullptr == S3D::GetSGNodeParent( item ) )
558 S3D::AddSGNodeChild( parent, item );
559 else
560 S3D::AddSGNodeRef( parent, item );
561
562 ++sL;
563 }
564}
565
566
567bool readIGES( Handle( TDocStd_Document ) & m_doc, const char* fname )
568{
569 IGESCAFControl_Reader reader;
570 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
571 reader.PrintCheckLoad( false, IFSelect_ItemsByEntity );
572
573 if( stat != IFSelect_RetDone )
574 return false;
575
576 // Enable file-defined shape precision
577 if( !Interface_Static::SetIVal( "read.precision.mode", 0 ) )
578 return false;
579
580 // set other translation options
581 reader.SetColorMode(true); // use model colors
582 reader.SetNameMode(false); // don't use IGES label names
583 reader.SetLayerMode(false); // ignore LAYER data
584
585 if( !reader.Transfer( m_doc ) )
586 return false;
587
588 // are there any shapes to translate?
589 if( reader.NbShapes() < 1 )
590 return false;
591
592 return true;
593}
594
595
596bool readSTEP( Handle( TDocStd_Document ) & m_doc, const char* fname, double aLinearDeflection )
597{
598 wxLogTrace( MASK_OCE, wxT( "Reading step file %s" ), fname );
599
600 STEPCAFControl_Reader reader;
601 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
602
603 if( stat != IFSelect_RetDone )
604 return false;
605
606 // Enable user-defined shape precision
607 if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
608 return false;
609
610 // Set the shape conversion precision (default 0.0001 has too many triangles)
611 if( !Interface_Static::SetRVal( "read.precision.val", aLinearDeflection ) )
612 return false;
613
614 // set other translation options
615 reader.SetColorMode( true ); // use model colors
616 reader.SetNameMode( false ); // don't use label names
617 reader.SetLayerMode( false ); // ignore LAYER data
618
619 if( !reader.Transfer( m_doc ) )
620 return false;
621
622 // are there any shapes to translate?
623 if( reader.NbRootsForTransfer() < 1 )
624 return false;
625
626 return true;
627}
628
629
630bool readSTEPZ( Handle( TDocStd_Document ) & m_doc, const char* aFileName, const S3D::MODEL_IMPORT_OPTIONS& aOptions,
631 MODEL_IMPORT_STATUS* aStatus )
632{
633 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
634 wxFFileInputStream ifile( fname.GetFullPath() );
635
636 wxString tempPath = CreateModelImportTempFileName( wxS( "kicad-step-" ) );
637 wxFileName outFile( tempPath );
638
639 MODEL_IMPORT_TEMP_FILE cleanup{ tempPath };
640
641 if( tempPath.empty() || !ifile.IsOk() )
642 return false;
643
644 wxFileOffset size = ifile.GetLength();
645 if( size == wxInvalidOffset )
646 return false;
647
648 if( static_cast<std::uint64_t>( size ) > aOptions.maxCompressedBytes
649 || static_cast<std::uint64_t>( size ) > std::numeric_limits<std::size_t>::max() )
650 {
651 if( aStatus )
652 aStatus->Set( S3D::MODEL_IMPORT_ERROR::TOO_LARGE, "Compressed STEP exceeds the configured limit" );
653
654 return false;
655 }
656
657 {
658 bool success = false;
659 wxFFileOutputStream ofile( outFile.GetFullPath() );
660
661 if( !ofile.IsOk() )
662 return false;
663
664 std::vector<char> buffer( static_cast<size_t>( size ) );
665
666 ifile.Read( buffer.data(), size );
667
668 if( ifile.LastRead() != static_cast<size_t>( size ) )
669 return false;
670
671 std::string expanded;
673 buffer.data(), static_cast<size_t>( size ), aOptions.maxExpandedBytes, expanded );
674
675 if( gzipResult == MODEL_IMPORT_GZIP_RESULT::TOO_LARGE )
676 {
677 if( aStatus )
678 aStatus->Set( S3D::MODEL_IMPORT_ERROR::TOO_LARGE, "Expanded STEP exceeds the configured limit" );
679
680 return false;
681 }
682
683 // A .stpz may be a plain zip rather than a gzip stream, so fall back before rejecting it.
684 if( gzipResult == MODEL_IMPORT_GZIP_RESULT::CORRUPT )
685 {
686 if( buffer.size() < 4 || buffer[0] != 'P' || buffer[1] != 'K' || buffer[2] != 3 || buffer[3] != 4 )
687 {
688 if( aStatus )
689 aStatus->Set( S3D::MODEL_IMPORT_ERROR::INVALID, "Compressed STEP archive is corrupt" );
690
691 return false;
692 }
693
694 ifile.Reset();
695 ifile.SeekI( 0 );
696 wxZipInputStream izipfile( ifile );
697 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
698
699 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
700 {
701 char chunk[65536];
702 std::uint64_t written = 0;
703
704 while( izipfile.CanRead() )
705 {
706 izipfile.Read( chunk, sizeof( chunk ) );
707 size_t count = izipfile.LastRead();
708
709 if( count > aOptions.maxExpandedBytes - written )
710 {
711 if( aStatus )
713 "Expanded STEP exceeds the configured limit" );
714
715 return false;
716 }
717
718 ofile.Write( chunk, count );
719
720 if( ofile.LastWrite() != count )
721 return false;
722
723 written += count;
724 }
725
726 success = true;
727 }
728 }
729 else
730 {
731 ofile.Write( expanded.data(), expanded.size() );
732 success = ofile.LastWrite() == expanded.size();
733 }
734
735 success = ofile.Close() && success;
736
737 if( !success )
738 return false;
739 }
740
741 bool retval = readSTEP( m_doc, outFile.GetFullPath().mb_str(), aOptions.stepLinearDeflection );
742
743 return retval;
744}
745
746
747SCENEGRAPH* LoadModel( char const* filename, const S3D::MODEL_IMPORT_OPTIONS* aOptions, MODEL_IMPORT_STATUS* aStatus )
748{
749 DATA data;
750
751 if( aOptions )
752 data.options = *aOptions;
753
754 data.status = aStatus;
755
756 data.m_app = XCAFApp_Application::GetApplication();
757 data.m_app->NewDocument( "MDTV-XCAF", data.m_doc );
758 FormatType modelFmt = fileType( filename );
759
760 if( ( data.options.format == S3D::MODEL_IMPORT_FORMAT::STEP && modelFmt != FMT_STEP && modelFmt != FMT_STPZ )
761 || ( data.options.format == S3D::MODEL_IMPORT_FORMAT::IGES && modelFmt != FMT_IGES ) )
762 return nullptr;
763
764 switch( modelFmt )
765 {
766 case FMT_IGES:
767 data.renderBoth = true;
768
769 if( !readIGES( data.m_doc, filename ) )
770 return nullptr;
771
772 break;
773
774 case FMT_STEP:
775 if( !readSTEP( data.m_doc, filename, data.options.stepLinearDeflection ) )
776 return nullptr;
777
778 break;
779
780 case FMT_STPZ:
781 if( !readSTEPZ( data.m_doc, filename, data.options, aStatus ) )
782 return nullptr;
783
784 break;
785
786
787 default:
788 return nullptr;
789 }
790
791 data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() );
792 data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() );
793
794 // Check if the log mask is enabled otherwise the dump routine may be expensive before the wxLog call
795 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
796 {
797 dumpLabels( data.m_doc->Main(), data.m_assy, data.m_color );
798 }
799
800 // retrieve all free shapes
801 NCollection_Sequence<TDF_Label> frshapes;
802 data.m_assy->GetFreeShapes( frshapes );
803
804 if( data.IsCanceled() )
805 return nullptr;
806
807 bool ret = false;
808
809 // create the top level SG node
810 IFSG_TRANSFORM topNode( true );
811 data.scene = topNode.GetRawPtr();
812
813 for( int i = 1; i <= frshapes.Length(); i++ )
814 {
815 if( data.IsCanceled() )
816 return nullptr;
817
818 const TDF_Label& label = frshapes.Value( i );
819
820 if( data.m_color->IsVisible( label ) )
821 {
822 if( processLabel( label, data, data.scene, nullptr ) )
823 ret = true;
824 }
825 }
826
827 if( !ret )
828 {
829 return nullptr;
830 }
831
832 SCENEGRAPH* scene = (SCENEGRAPH*)data.scene;
833
834 // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
835#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
836 if( data.scene )
837 {
838 wxFileName fn( wxString::FromUTF8Unchecked( filename ) );
839 wxString output;
840
841 if( FMT_STEP == modelFmt )
842 output = wxT( "_step-" );
843 else
844 output = wxT( "_iges-" );
845
846 output.append( fn.GetName() );
847 output.append( wxT( ".wrl" ) );
848 S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true );
849 }
850#endif
851
852 // set to NULL to prevent automatic destruction of the scene data
853 data.scene = nullptr;
854
855 return scene;
856}
857
858
859bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
860 std::vector<SGNODE*>* items, Quantity_ColorRGBA* color )
861{
862 TopoDS_Iterator it;
863 bool ret = false;
864
865 wxLogTrace( MASK_OCE, wxT( "Processing shell" ) );
866 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
867 {
868 if( data.IsCanceled() )
869 return false;
870
871 const TopoDS_Face& face = TopoDS::Face( it.Value() );
872
873 if( processFace( face, data, parent, items, color ) )
874 ret = true;
875 }
876
877 return ret;
878}
879
880
881bool processSolidOrShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
882 std::vector< SGNODE* >* items )
883{
884 TDF_Label label;
885 std::string partID;
886 Quantity_ColorRGBA col;
887 Quantity_ColorRGBA* lcolor = nullptr;
888
889 data.hasSolid = shape.ShapeType() == TopAbs_SOLID;
890
891 wxLogTrace( MASK_OCE, wxT( "Processing solid" ) );
892
893 // Search the whole model first to make sure something exists (may or may not have color)
894 if( !data.m_assy->Search( shape, label ) )
895 {
896 static std::atomic_int i{ 0 };
897 std::ostringstream ostr;
898 ostr << "KMISC_" << i.fetch_add( 1, std::memory_order_relaxed );
899 partID = ostr.str();
900 }
901 else
902 {
903 bool found_color = false;
904
905 if( getColor( data, label, col ) )
906 {
907 found_color = true;
908 lcolor = &col;
909 }
910
911 // If the top-level label doesn't have the color information, search components
912 if( !found_color )
913 {
914 if( data.m_assy->Search( shape, label, false, true, true ) &&
915 getColor( data, label, col ) )
916 {
917 found_color = true;
918 lcolor = &col;
919 }
920 }
921
922 // If the components do not have color information, search all components without location
923 if( !found_color )
924 {
925 if( data.m_assy->Search( shape, label, false, false,
926 true ) &&
927 getColor( data, label, col ) )
928 {
929 found_color = true;
930 lcolor = &col;
931 }
932 }
933
934 // Our last chance to find the color looks for color as a subshape of top-level simple
935 // shapes.
936 if( !found_color )
937 {
938 if( data.m_assy->Search( shape, label, false, false,
939 false ) &&
940 getColor( data, label, col ) )
941 {
942 found_color = true;
943 lcolor = &col;
944 }
945 }
946
947 getTag( label, partID );
948 }
949
950 TopoDS_Iterator it;
951 IFSG_TRANSFORM childNode( parent );
952 SGNODE* pptr = childNode.GetRawPtr();
953 bool ret = false;
954
955
956 std::vector< SGNODE* >* component = nullptr;
957
958 if( !partID.empty() )
959 data.GetShape( partID, component );
960
961 if( component )
962 {
963 addItems( pptr, component );
964
965 if( nullptr != items )
966 items->push_back( pptr );
967 }
968
969 // instantiate resulting object
970 std::vector<SGNODE*> itemList;
971
972 TopAbs_ShapeEnum stype = shape.ShapeType();
973 if( stype == TopAbs_SHELL )
974 {
975 if( processShell( shape, data, pptr, &itemList, lcolor ) )
976 ret = true;
977 }
978 else
979 {
980
981 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
982 {
983 const TopoDS_Shape& subShape = it.Value();
984
985 if( subShape.ShapeType() == TopAbs_SHELL )
986 {
987 if( processShell( subShape, data, pptr, &itemList, lcolor ) )
988 ret = true;
989 }
990 else
991 {
992 wxLogTrace( MASK_OCE, wxT( "Unsupported subshape in solid" ) );
993 }
994 }
995 }
996
997 if( !ret )
998 childNode.Destroy();
999 else if( nullptr != items )
1000 items->push_back( pptr );
1001
1002 return ret;
1003}
1004
1005
1006bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
1007 std::vector<SGNODE*>* aItems )
1008{
1009 if( aData.IsCanceled() )
1010 return false;
1011
1012 std::string labelTag;
1013
1014 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
1015 {
1016 // can be expensive, guard it if we aren't logging
1017 getTag( aLabel, labelTag );
1018 }
1019
1020 wxLogTrace( MASK_OCE, wxT( "Processing label %s" ), labelTag );
1021
1022 TopoDS_Shape originalShape;
1023 TDF_Label shapeLabel = aLabel;
1024
1025 if( !aData.m_assy->GetShape( shapeLabel, originalShape ) )
1026 {
1027 return false;
1028 }
1029
1030 TopoDS_Shape shape = originalShape;
1031
1032 if( aData.m_assy->IsReference( aLabel ) )
1033 {
1034 wxLogTrace( MASK_OCE, wxT( "Label %s is ref, trying to pull up referred label" ),
1035 labelTag );
1036
1037 if( !aData.m_assy->GetReferredShape( aLabel, shapeLabel ) )
1038 {
1039 return false;
1040 }
1041
1042 labelTag = static_cast<int>( shapeLabel.Tag() );
1043 // wxLogTrace( MASK_OCE, wxT( "Label %s referred" ), labelTag );
1044
1045 if( !aData.m_assy->GetShape( shapeLabel, shape ) )
1046 {
1047 return false;
1048 }
1049 }
1050
1051 // Now let's see if the original label has a location
1052 // Labels can be used to place copies of other labels at a specific location
1053 IFSG_TRANSFORM childNode( aParent );
1054 SGNODE* pptr = childNode.GetRawPtr();
1055 const TopLoc_Location& loc = originalShape.Location();
1056
1057 if( !loc.IsIdentity() )
1058 {
1059 wxLogTrace( MASK_OCE, wxT( "Label %d has location" ), static_cast<int>( aLabel.Tag() ) );
1060 gp_Trsf T = loc.Transformation();
1061 gp_XYZ coord = T.TranslationPart();
1062 childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
1063 wxLogTrace( MASK_OCE, wxT( "Translation %f, %f, %f" ), coord.X(), coord.Y(), coord.Z() );
1064 gp_XYZ axis;
1065 double angle;
1066
1067 if( T.GetRotation( axis, angle ) )
1068 {
1069 childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
1070 wxLogTrace( MASK_OCE, wxT( "Rotation %f, %f, %f, angle %f" ),
1071 axis.X(), axis.Y(), axis.Z(), angle );
1072 }
1073 }
1074
1075 TopAbs_ShapeEnum stype = shape.ShapeType();
1076 bool ret = false;
1077 aData.hasSolid = false;
1078
1079 switch( stype )
1080 {
1081 case TopAbs_COMPOUND:
1082 {
1083 // assemblies will report a shape type of compound which isn't what we are after
1084 // We still process assembly children, but they should only be label references to the actual shapes.
1085 if( !aData.m_assy->IsAssembly( shapeLabel ) )
1086 {
1087 TopExp_Explorer xp;
1088
1089 for( xp.Init( shape, TopAbs_SOLID ); xp.More(); xp.Next() )
1090 {
1091 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1092 ret = true;
1093 }
1094
1095 // Get all shells, avoid those that may be attached to solids
1096 for( xp.Init( shape, TopAbs_SHELL, TopAbs_SOLID ); xp.More(); xp.Next() )
1097 {
1098 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1099 ret = true;
1100 }
1101
1102 // Get all faces, avoid those that may be attached to shells
1103 for( xp.Init( shape, TopAbs_FACE, TopAbs_SHELL ); xp.More(); xp.Next() )
1104 {
1105 const TopoDS_Face& face = TopoDS::Face( xp.Current() );
1106 processFace( face, aData, pptr, aItems, nullptr );
1107 ret = true;
1108 }
1109 }
1110 }
1111 break;
1112
1113 case TopAbs_SOLID:
1114 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1115 ret = true;
1116
1117 break;
1118
1119 case TopAbs_SHELL:
1120 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1121 ret = true;
1122
1123 break;
1124
1125 case TopAbs_FACE:
1126 if( processFace( TopoDS::Face( shape ), aData, pptr, aItems, nullptr ) )
1127 ret = true;
1128
1129 break;
1130
1131 default:
1132 break;
1133 }
1134
1135 if( nullptr != aItems )
1136 aItems->push_back( pptr );
1137
1138 if( !aData.m_assy->IsSimpleShape( shapeLabel ) && shapeLabel.HasChild() )
1139 {
1140 wxLogTrace( MASK_OCE, wxT( "Label %s has children" ), labelTag );
1141 TDF_ChildIterator it;
1142
1143 for( it.Initialize( shapeLabel ); it.More(); it.Next() )
1144 {
1145 if( processLabel( it.Value(), aData, pptr, aItems ) )
1146 ret = true;
1147 }
1148 }
1149
1150 return ret;
1151}
1152
1153
1154bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, std::vector<SGNODE*>* items,
1155 Quantity_ColorRGBA* color )
1156{
1157 if( true == face.IsNull() )
1158 return false;
1159
1160 bool reverse = ( face.Orientation() == TopAbs_REVERSED );
1161 SGNODE* ashape = nullptr;
1162 std::string partID;
1163 TDF_Label label;
1164
1165 bool useBothSides = false;
1166
1167 // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant
1168 // of a SOLID then hasSolid = false and we must render both sides
1169 if( data.renderBoth || !data.hasSolid )
1170 useBothSides = true;
1171
1172 if( data.m_assy->FindShape( face, label, false ) )
1173 getTag( label, partID );
1174
1175 if( !partID.empty() )
1176 ashape = data.GetFace( partID );
1177
1178 if( ashape )
1179 {
1180 if( nullptr == S3D::GetSGNodeParent( ashape ) )
1181 S3D::AddSGNodeChild( parent, ashape );
1182 else
1183 S3D::AddSGNodeRef( parent, ashape );
1184
1185 if( nullptr != items )
1186 items->push_back( ashape );
1187
1188 if( useBothSides )
1189 {
1190 std::string id2 = partID;
1191 id2.append( "b" );
1192 SGNODE* shapeB = data.GetFace( id2 );
1193
1194 if( nullptr == S3D::GetSGNodeParent( shapeB ) )
1195 S3D::AddSGNodeChild( parent, shapeB );
1196 else
1197 S3D::AddSGNodeRef( parent, shapeB );
1198
1199 if( nullptr != items )
1200 items->push_back( shapeB );
1201 }
1202
1203 return true;
1204 }
1205
1206 TopLoc_Location loc;
1207 bool isTessellate (false);
1208 Handle( Poly_Triangulation ) triangulation = BRep_Tool::Triangulation( face, loc );
1209 const double linDeflection = data.options.stepLinearDeflection;
1210
1211 if( triangulation.IsNull() || triangulation->Deflection() > linDeflection + Precision::Confusion() )
1212 isTessellate = true;
1213
1214 if( isTessellate )
1215 {
1216 BRepMesh_IncrementalMesh IM(face, linDeflection, false,
1217 glm::radians( data.options.stepAngularDeflectionDegrees ) );
1218 triangulation = BRep_Tool::Triangulation( face, loc );
1219 }
1220
1221 if( data.IsCanceled() )
1222 return false;
1223
1224 if( triangulation.IsNull() == true )
1225 return false;
1226
1227 Quantity_ColorRGBA lcolor;
1228
1229 // check for a face color; this has precedence over SOLID colors
1230 if( data.m_color->GetColor( face, XCAFDoc_ColorSurf, lcolor )
1231 || data.m_color->GetColor( face, XCAFDoc_ColorCurv, lcolor )
1232 || data.m_color->GetColor( face, XCAFDoc_ColorGen, lcolor ) )
1233 {
1234 color = &lcolor;
1235 }
1236
1237 std::vector< SGPOINT > vertices;
1238 std::vector< int > indices;
1239 std::vector< int > indices2;
1240
1241 // The triangulation location transform converts local triangulation coordinates to face
1242 // coordinates. This must be applied to get correct vertex positions. Skip identity transforms.
1243 bool applyTransform = !loc.IsIdentity();
1244 gp_Trsf tx = applyTransform ? loc.Transformation() : gp_Trsf();
1245
1246 for( int i = 1; i <= triangulation->NbNodes(); i++ )
1247 {
1248 if( ( i & 0x3FF ) == 0 && data.IsCanceled() )
1249 return false;
1250 gp_XYZ v = applyTransform ? triangulation->Node( i ).Transformed( tx ).Coord()
1251 : triangulation->Node( i ).Coord();
1252 vertices.emplace_back( v.X(), v.Y(), v.Z() );
1253 }
1254
1255 for( int i = 1; i <= triangulation->NbTriangles(); i++ )
1256 {
1257 if( ( i & 0x3FF ) == 0 && data.IsCanceled() )
1258 return false;
1259
1260 int a, b, c;
1261 triangulation->Triangle(i).Get(a, b, c);
1262 a--;
1263
1264 if( reverse )
1265 {
1266 int tmp = b - 1;
1267 b = c - 1;
1268 c = tmp;
1269 }
1270 else
1271 {
1272 b--;
1273 c--;
1274 }
1275
1276 indices.push_back( a );
1277 indices.push_back( b );
1278 indices.push_back( c );
1279
1280 if( useBothSides )
1281 {
1282 indices2.push_back( b );
1283 indices2.push_back( a );
1284 indices2.push_back( c );
1285 }
1286 }
1287
1288 if( vertices.empty() || indices.empty() )
1289 return false;
1290
1291 SGNODE* ocolor = data.GetColor( color );
1292
1293 // create a SHAPE and attach the color and data,
1294 // then attach the shape to the parent and return TRUE
1295 IFSG_SHAPE vshape( true );
1296 IFSG_FACESET vface( vshape );
1297 IFSG_COORDS vcoords( vface );
1298 IFSG_COORDINDEX coordIdx( vface );
1299
1300 if( nullptr == S3D::GetSGNodeParent( ocolor ) )
1301 S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor );
1302 else
1303 S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor );
1304
1305 vcoords.SetCoordsList( vertices.size(), vertices.data() );
1306 coordIdx.SetIndices( indices.size(), indices.data() );
1307 vface.CalcNormals( nullptr );
1308 vshape.SetParent( parent );
1309
1310 if( !partID.empty() )
1311 data.faces.emplace( partID, vshape.GetRawPtr() );
1312
1313 // The outer surface of an IGES model is indeterminate so
1314 // we must render both sides of a surface.
1315 if( useBothSides )
1316 {
1317 std::string id2 = partID;
1318 id2.append( "b" );
1319 IFSG_SHAPE vshape2( true );
1320 IFSG_FACESET vface2( vshape2 );
1321 IFSG_COORDS vcoords2( vface2 );
1322 IFSG_COORDINDEX coordIdx2( vface2 );
1323 S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor );
1324
1325 vcoords2.SetCoordsList( vertices.size(), vertices.data() );
1326 coordIdx2.SetIndices( indices2.size(), indices2.data() );
1327 vface2.CalcNormals( nullptr );
1328 vshape2.SetParent( parent );
1329
1330 if( !partID.empty() )
1331 data.faces.emplace( id2, vshape2.GetRawPtr() );
1332 }
1333
1334 return true;
1335}
1336
1337
1338SCENEGRAPH* LoadModel( const char* aFileName, const S3D::MODEL_IMPORT_OPTIONS* aOptions )
1339{
1342 MODEL_IMPORT_STATUS status;
1343
1344 try
1345 {
1346 if( SCENEGRAPH* scene = LoadModel( aFileName, aOptions, &status ) )
1347 return scene;
1348 }
1349 catch( ... )
1350 {
1351 return nullptr;
1352 }
1353
1354 wxLogTrace( MASK_OCE, wxT( "Model import failed: %s" ), status.diagnostic.c_str() );
1355 return nullptr;
1356}
const char * name
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetShininess(float aShininess) noexcept
bool NewNode(SGNODE *aParent) override
Create a new node to associate with this wrapper.
bool SetAmbient(float aRVal, float aGVal, float aBVal)
bool SetTransparency(float aTransparency) noexcept
The wrapper for SGCOORDINDEX.
The wrapper for SGCOORDS.
Definition ifsg_coords.h:36
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
The wrapper for the SGFACESET class.
bool CalcNormals(SGNODE **aPtr)
bool SetIndices(size_t nIndices, int *aIndexList)
Set the number of indices and creates a copy of the given index data.
bool SetParent(SGNODE *aParent)
Set the parent SGNODE of this object.
Definition ifsg_node.cpp:83
SGNODE * GetRawPtr(void) noexcept
Return the raw internal SGNODE pointer.
Definition ifsg_node.cpp:61
void Destroy(void)
Delete the object held by this wrapper.
Definition ifsg_node.cpp:51
The wrapper for the SGSHAPE class.
Definition ifsg_shape.h:36
The wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
bool SetTranslation(const SGPOINT &aTranslation) noexcept
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
Scoped holder for the recursive model import lock.
Definition ifsg_api.h:59
Define the basic data set required to represent a 3D model.
Definition scenegraph.h:41
The base class of all Scene Graph nodes.
Definition sg_node.h:71
collects header files for all SG* wrappers and the API
Handle(KICAD3D_INFO) KICAD3D_INFO
bool processShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
std::map< std::string, std::vector< SGNODE * > > NODEMAP
Definition loadmodel.cpp:94
bool processFace(const TopoDS_Face &face, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
std::map< COLORKEY, IFSG_APPEARANCE > COLORMAP
Definition loadmodel.cpp:92
std::pair< std::string, std::vector< SGNODE * > > NODEITEM
Definition loadmodel.cpp:95
std::array< double, 4 > COLORKEY
Definition loadmodel.cpp:91
bool readSTEP(Handle(TDocStd_Document) &m_doc, const char *fname, double aLinearDeflection)
bool getColor(DATA &data, TDF_Label label, Quantity_ColorRGBA &color)
FormatType
@ FMT_STPZ
static int colorFloatToDecimal(float aVal)
#define MASK_OCE
Definition loadmodel.cpp:88
void getTag(const TDF_Label &aLabel, std::string &aTag)
Gets the absolute tag string for a given label in the form of ##:##:##:##.
void addItems(SGNODE *parent, std::vector< SGNODE * > *lp)
std::string getShapeName(TopAbs_ShapeEnum aShape)
Gets a string for a given TopAbs_ShapeEnum element.
static void printLabel(TDF_Label aLabel, Handle(XCAFDoc_ShapeTool) aShapeTool, Handle(XCAFDoc_ColorTool) aColorTool, const char *aPreMsg=nullptr)
Get a string for a given TopAbs_ShapeEnum element.
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.
FormatType fileType(const char *aFileName)
static std::ostream & operator<<(std::ostream &aOStream, const Quantity_ColorRGBA &aColor)
#define OCC_COLOR_SPACE
bool processSolidOrShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
static wxString getLabelName(const TDF_Label &aLabel)
bool processLabel(const TDF_Label &aLabel, DATA &aData, SGNODE *aParent, std::vector< SGNODE * > *aItems)
SCENEGRAPH * LoadModel(char const *filename, const S3D::MODEL_IMPORT_OPTIONS *aOptions, MODEL_IMPORT_STATUS *aStatus)
std::map< std::string, SGNODE * > FACEMAP
Definition loadmodel.cpp:93
bool readSTEPZ(Handle(TDocStd_Document) &m_doc, const char *aFileName, const S3D::MODEL_IMPORT_OPTIONS &aOptions, MODEL_IMPORT_STATUS *aStatus)
bool readIGES(Handle(TDocStd_Document) &m_doc, const char *fname)
wxString CreateModelImportTempFileName(const wxString &aPrefix)
MODEL_IMPORT_GZIP_RESULT DecompressBoundedModel(const char *aData, std::size_t aSize, std::uint64_t aMaxExpandedBytes, std::string &aExpanded)
Expand a gzip stream into aExpanded, refusing anything over aMaxExpandedBytes.
MODEL_IMPORT_GZIP_RESULT
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Write out the given node and its subnodes to a VRML2 file.
Definition ifsg_api.cpp:91
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition ifsg_api.cpp:518
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Delete the given SG* class node.
Definition ifsg_api.cpp:169
SGLIB_API bool AddSGNodeChild(SGNODE *aParent, SGNODE *aChild)
Definition ifsg_api.cpp:536
SGLIB_API bool AddSGNodeRef(SGNODE *aParent, SGNODE *aChild)
Definition ifsg_api.cpp:527
static SGNODE * getColor(IFSG_SHAPE &shape, int colorIdx)
@ FMT_STEP
@ FMT_IGES
@ FMT_NONE
Handle(XCAFApp_Application) m_app
bool IsCanceled()
COLORMAP colors
Handle(XCAFDoc_ColorTool) m_color
bool renderBoth
SGNODE * GetFace(const std::string &id)
Handle(TDocStd_Document) m_doc
bool GetShape(const std::string &id, std::vector< SGNODE * > *&listPtr)
IFSG_APPEARANCE defaultColor
Wrapper so the node clears it if a shape that took ownership is destroyed first.
S3D::MODEL_IMPORT_OPTIONS options
Handle(XCAFDoc_ShapeTool) m_assy
MODEL_IMPORT_STATUS * status
NODEMAP shapes
SGNODE * scene
bool hasSolid
FACEMAP faces
SGNODE * GetColor(Quantity_ColorRGBA *colorObj)
void Set(S3D::MODEL_IMPORT_ERROR aError, const char *aDiagnostic)
Remove the scratch file an importer expanded a compressed model into.
MODEL_IMPORT_FORMAT format
std::uint64_t maxCompressedBytes
std::uint64_t maxExpandedBytes