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-2023 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 memset( iline, 0, 82 );
300 ifile.Read( iline, 82 );
301 iline[81] = 0; // ensure NULL termination when string is too long
302
303 // check for STEP in Part 21 format
304 // (this can give false positives since Part 21 is not exclusively STEP)
305 if( !strncmp( iline, "ISO-10303-21;", 13 ) )
306 return FMT_STEP;
307
308 std::string fstr = iline;
309
310 // check for STEP in XML format
311 // (this can give both false positive and false negatives)
312 if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos )
313 return FMT_STEP;
314
315 // Note: this is a very simple test which can yield false positives; the only
316 // sure method for determining if a file *not* an IGES model is to attempt
317 // to load it.
318 if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
319 return FMT_IGES;
320
321 return FMT_NONE;
322}
323
324
331void getTag( const TDF_Label& aLabel, std::string& aTag )
332{
333 std::ostringstream ostr;
334
335 if( aLabel.IsNull() )
336 {
337 wxLogTrace( MASK_OCE, wxT( "Null label passed to getTag" ) );
338 return;
339 }
340
341 TColStd_ListOfInteger tagList;
342 TDF_Tool::TagList( aLabel, tagList );
343
344 for( TColStd_ListOfInteger::Iterator it( tagList ); it.More(); it.Next() )
345 {
346 ostr << it.Value();
347 ostr << ":";
348 }
349
350 aTag = ostr.str();
351 aTag.pop_back(); // kill the last colon
352}
353
354
355static wxString getLabelName( const TDF_Label& aLabel )
356{
357 wxString txt;
358 Handle( TDataStd_Name ) name;
359 if( !aLabel.IsNull() && aLabel.FindAttribute( TDataStd_Name::GetID(), name ) )
360 {
361 TCollection_ExtendedString extstr = name->Get();
362 char* str = new char[extstr.LengthOfCString() + 1];
363 extstr.ToUTF8CString( str );
364
365 txt = wxString::FromUTF8( str );
366 delete[] str;
367 txt = txt.Trim();
368 }
369 return txt;
370}
371
372
378std::string getShapeName( TopAbs_ShapeEnum aShape )
379{
380 switch( aShape )
381 {
382 case TopAbs_COMPOUND: return "COMPOUND";
383 case TopAbs_COMPSOLID: return "COMPSOLID";
384 case TopAbs_SOLID: return "SOLID";
385 case TopAbs_SHELL: return "SHELL";
386 case TopAbs_FACE: return "FACE";
387 case TopAbs_WIRE: return "WIRE";
388 case TopAbs_EDGE: return "EDGE";
389 case TopAbs_VERTEX: return "VERTEX";
390 case TopAbs_SHAPE: return "SHAPE";
391 }
392
393 return "UNKNOWN";
394}
395
396static int colorFloatToDecimal( float aVal )
397{
398 return aVal * 255;
399}
400
401
402static inline std::ostream& operator<<( std::ostream& aOStream, const Quantity_ColorRGBA& aColor )
403{
404 Quantity_Color rgb = aColor.GetRGB();
405
406 return aOStream << "rgba(" << colorFloatToDecimal( rgb.Red() ) << ","
407 << colorFloatToDecimal( rgb.Green() ) << ","
408 << colorFloatToDecimal( rgb.Blue() ) << ","
409 << colorFloatToDecimal( aColor.Alpha() )
410 << ")";
411}
412
413
422static void printLabel( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
423 Handle( XCAFDoc_ColorTool ) aColorTool, const char* aPreMsg = nullptr )
424{
425 if( aLabel.IsNull() )
426 return;
427
428 if( !aPreMsg )
429 aPreMsg = "Label: ";
430
431 TCollection_AsciiString entry;
432 TDF_Tool::Entry( aLabel, entry );
433 std::ostringstream ss;
434 ss << aPreMsg << entry << ", " << getLabelName( aLabel )
435 << ( aShapeTool->IsShape( aLabel ) ? ", shape" : "" )
436 << ( aShapeTool->IsTopLevel( aLabel ) ? ", topLevel" : "" )
437 << ( aShapeTool->IsFree( aLabel ) ? ", free" : "" )
438 << ( aShapeTool->IsAssembly( aLabel ) ? ", assembly" : "" )
439 << ( aShapeTool->IsSimpleShape( aLabel ) ? ", simple" : "" )
440 << ( aShapeTool->IsCompound( aLabel ) ? ", compound" : "" )
441 << ( aShapeTool->IsReference( aLabel ) ? ", reference" : "" )
442 << ( aShapeTool->IsComponent( aLabel ) ? ", component" : "" )
443 << ( aShapeTool->IsSubShape( aLabel ) ? ", subshape" : "" );
444
445 if( aShapeTool->IsSubShape( aLabel ) )
446 {
447 auto shape = aShapeTool->GetShape( aLabel );
448 if( !shape.IsNull() )
449 ss << ", " << getShapeName( shape.ShapeType() );
450 }
451
452 if( aShapeTool->IsShape( aLabel ) )
453 {
454 Quantity_ColorRGBA c;
455 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorGen, c ) )
456 ss << ", gc: " << c;
457 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorSurf, c ) )
458 ss << ", sc: " << c;
459 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorCurv, c ) )
460 ss << ", cc: " << c;
461 }
462
463 wxLogTrace( MASK_OCE, ss.str().c_str() );
464}
465
466
475static void dumpLabels( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
476 Handle( XCAFDoc_ColorTool ) aColorTool, int aDepth = 0 )
477{
478 std::string indent( aDepth * 2, ' ' );
479 printLabel( aLabel, aShapeTool, aColorTool, indent.c_str() );
480 TDF_ChildIterator it;
481 for( it.Initialize( aLabel ); it.More(); it.Next() )
482 dumpLabels( it.Value(), aShapeTool, aColorTool, aDepth + 1 );
483}
484
485
486bool getColor( DATA& data, TDF_Label label, Quantity_ColorRGBA& color )
487{
488 while( true )
489 {
490 if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) )
491 return true;
492 else if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) )
493 return true;
494 else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) )
495 return true;
496
497 label = label.Father();
498
499 if( label.IsNull() )
500 break;
501 };
502
503 return false;
504}
505
506
507void addItems( SGNODE* parent, std::vector< SGNODE* >* lp )
508{
509 if( nullptr == lp )
510 return;
511
512 std::vector< SGNODE* >::iterator sL = lp->begin();
513 std::vector< SGNODE* >::iterator eL = lp->end();
514 SGNODE* item;
515
516 while( sL != eL )
517 {
518 item = *sL;
519
520 if( nullptr == S3D::GetSGNodeParent( item ) )
521 S3D::AddSGNodeChild( parent, item );
522 else
523 S3D::AddSGNodeRef( parent, item );
524
525 ++sL;
526 }
527}
528
529
530bool readIGES( Handle( TDocStd_Document ) & m_doc, const char* fname )
531{
532 IGESCAFControl_Reader reader;
533 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
534 reader.PrintCheckLoad( Standard_False, IFSelect_ItemsByEntity );
535
536 if( stat != IFSelect_RetDone )
537 return false;
538
539 // Enable file-defined shape precision
540 if( !Interface_Static::SetIVal( "read.precision.mode", 0 ) )
541 return false;
542
543 // set other translation options
544 reader.SetColorMode(true); // use model colors
545 reader.SetNameMode(false); // don't use IGES label names
546 reader.SetLayerMode(false); // ignore LAYER data
547
548 if( !reader.Transfer( m_doc ) )
549 {
550 if( m_doc->CanClose() == CDM_CCS_OK )
551 m_doc->Close();
552
553 return false;
554 }
555
556 // are there any shapes to translate?
557 if( reader.NbShapes() < 1 )
558 {
559 if( m_doc->CanClose() == CDM_CCS_OK )
560 m_doc->Close();
561
562 return false;
563 }
564
565 return true;
566}
567
568
569bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname )
570{
571 wxLogTrace( MASK_OCE, wxT( "Reading step file %s" ), fname );
572
573 STEPCAFControl_Reader reader;
574 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
575
576 if( stat != IFSelect_RetDone )
577 return false;
578
579 // Enable user-defined shape precision
580 if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
581 return false;
582
583 // Set the shape conversion precision (default 0.0001 has too many triangles)
584 if( !Interface_Static::SetRVal( "read.precision.val", ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection ) )
585 return false;
586
587 // set other translation options
588 reader.SetColorMode( true ); // use model colors
589 reader.SetNameMode( false ); // don't use label names
590 reader.SetLayerMode( false ); // ignore LAYER data
591
592 if( !reader.Transfer( m_doc ) )
593 {
594 if( m_doc->CanClose() == CDM_CCS_OK )
595 m_doc->Close();
596
597 return false;
598 }
599
600 // are there any shapes to translate?
601 if( reader.NbRootsForTransfer() < 1 )
602 {
603 if( m_doc->CanClose() == CDM_CCS_OK )
604 m_doc->Close();
605
606 return false;
607 }
608
609 return true;
610}
611
612
613bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName )
614{
615 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
616 wxFFileInputStream ifile( fname.GetFullPath() );
617
618 wxFileName outFile( fname );
619
620 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
621 outFile.SetExt( wxT( "STEP" ) );
622
623 wxFileOffset size = ifile.GetLength();
624 wxBusyCursor busycursor;
625
626 if( size == wxInvalidOffset )
627 return false;
628
629 {
630 bool success = false;
631 wxFFileOutputStream ofile( outFile.GetFullPath() );
632
633 if( !ofile.IsOk() )
634 return false;
635
636 char *buffer = new char[size];
637
638 ifile.Read( buffer, size);
639 std::string expanded;
640
641 try
642 {
643 expanded = gzip::decompress( buffer, size );
644 success = true;
645 }
646 catch(...)
647 {}
648
649 if( expanded.empty() )
650 {
651 ifile.Reset();
652 ifile.SeekI( 0 );
653 wxZipInputStream izipfile( ifile );
654 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
655
656 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
657 {
658 izipfile.Read( ofile );
659 success = true;
660 }
661 }
662 else
663 {
664 ofile.Write( expanded.data(), expanded.size() );
665 }
666
667 delete[] buffer;
668 ofile.Close();
669
670 if( !success )
671 return false;
672 }
673
674 bool retval = readSTEP( m_doc, outFile.GetFullPath().mb_str() );
675
676 // Cleanup our temporary file
677 wxRemoveFile( outFile.GetFullPath() );
678
679 return retval;
680}
681
682
683SCENEGRAPH* LoadModel( char const* filename )
684{
685 DATA data;
686
687 Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
688 m_app->NewDocument( "MDTV-XCAF", data.m_doc );
689 FormatType modelFmt = fileType( filename );
690
691 switch( modelFmt )
692 {
693 case FMT_IGES:
694 data.renderBoth = true;
695
696 if( !readIGES( data.m_doc, filename ) )
697 return nullptr;
698
699 break;
700
701 case FMT_STEP:
702 if( !readSTEP( data.m_doc, filename ) )
703 return nullptr;
704
705 break;
706
707 case FMT_STPZ:
708 if( !readSTEPZ( data.m_doc, filename ) )
709 return nullptr;
710
711 break;
712
713
714 default:
715 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
716 m_app->Close( data.m_doc );
717
718 return nullptr;
719 break;
720 }
721
722 data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() );
723 data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() );
724
725 // Check if the log mask is enabled otherwise the dump routine may be expensive before the wxLog call
726 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
727 {
728 dumpLabels( data.m_doc->Main(), data.m_assy, data.m_color );
729 }
730
731 // retrieve all free shapes
732 TDF_LabelSequence frshapes;
733 data.m_assy->GetFreeShapes( frshapes );
734
735 bool ret = false;
736
737 // create the top level SG node
738 IFSG_TRANSFORM topNode( true );
739 data.scene = topNode.GetRawPtr();
740
741 for( Standard_Integer i = 1; i <= frshapes.Length(); i++ )
742 {
743 const TDF_Label& label = frshapes.Value( i );
744
745 if( data.m_color->IsVisible( label ) )
746 {
747 if( processLabel( label, data, data.scene, nullptr ) )
748 ret = true;
749 }
750 }
751
752 if( !ret )
753 {
754 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
755 m_app->Close( data.m_doc );
756
757 return nullptr;
758 }
759
760 SCENEGRAPH* scene = (SCENEGRAPH*)data.scene;
761
762 // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
763#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
764 if( data.scene )
765 {
766 wxFileName fn( wxString::FromUTF8Unchecked( filename ) );
767 wxString output;
768
769 if( FMT_STEP == modelFmt )
770 output = wxT( "_step-" );
771 else
772 output = wxT( "_iges-" );
773
774 output.append( fn.GetName() );
775 output.append( wxT( ".wrl" ) );
776 S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true );
777 }
778#endif
779
780 // set to NULL to prevent automatic destruction of the scene data
781 data.scene = nullptr;
782
783 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
784 m_app->Close( data.m_doc );
785
786 return scene;
787}
788
789
790bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
791 std::vector<SGNODE*>* items, Quantity_ColorRGBA* color )
792{
793 TopoDS_Iterator it;
794 bool ret = false;
795
796 wxLogTrace( MASK_OCE, wxT( "Processing shell" ) );
797 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
798 {
799 const TopoDS_Face& face = TopoDS::Face( it.Value() );
800
801 if( processFace( face, data, parent, items, color ) )
802 ret = true;
803 }
804
805 return ret;
806}
807
808
809bool processSolidOrShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
810 std::vector< SGNODE* >* items )
811{
812 TDF_Label label;
813 std::string partID;
814 Quantity_ColorRGBA col;
815 Quantity_ColorRGBA* lcolor = nullptr;
816
817 data.hasSolid = shape.ShapeType() == TopAbs_SOLID;
818
819 wxLogTrace( MASK_OCE, wxT( "Processing solid" ) );
820
821 // Search the whole model first to make sure something exists (may or may not have color)
822 if( !data.m_assy->Search( shape, label ) )
823 {
824 static int i = 0;
825 std::ostringstream ostr;
826 ostr << "KMISC_" << i++;
827 partID = ostr.str();
828 }
829 else
830 {
831 bool found_color = false;
832
833 if( getColor( data, label, col ) )
834 {
835 found_color = true;
836 lcolor = &col;
837 }
838
839 // If the top-level label doesn't have the color information, search components
840 if( !found_color )
841 {
842 if( data.m_assy->Search( shape, label, Standard_False, Standard_True, Standard_True ) &&
843 getColor( data, label, col ) )
844 {
845 found_color = true;
846 lcolor = &col;
847 }
848 }
849
850 // If the components do not have color information, search all components without location
851 if( !found_color )
852 {
853 if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
854 Standard_True ) &&
855 getColor( data, label, col ) )
856 {
857 found_color = true;
858 lcolor = &col;
859 }
860 }
861
862 // Our last chance to find the color looks for color as a subshape of top-level simple
863 // shapes.
864 if( !found_color )
865 {
866 if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
867 Standard_False ) &&
868 getColor( data, label, col ) )
869 {
870 found_color = true;
871 lcolor = &col;
872 }
873 }
874
875 getTag( label, partID );
876 }
877
878 TopoDS_Iterator it;
879 IFSG_TRANSFORM childNode( parent );
880 SGNODE* pptr = childNode.GetRawPtr();
881 bool ret = false;
882
883
884 std::vector< SGNODE* >* component = nullptr;
885
886 if( !partID.empty() )
887 data.GetShape( partID, component );
888
889 if( component )
890 {
891 addItems( pptr, component );
892
893 if( nullptr != items )
894 items->push_back( pptr );
895 }
896
897 // instantiate resulting object
898 std::vector<SGNODE*> itemList;
899
900 TopAbs_ShapeEnum stype = shape.ShapeType();
901 if( stype == TopAbs_SHELL )
902 {
903 if( processShell( shape, data, pptr, &itemList, lcolor ) )
904 ret = true;
905 }
906 else
907 {
908
909 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
910 {
911 const TopoDS_Shape& subShape = it.Value();
912
913 if( subShape.ShapeType() == TopAbs_SHELL )
914 {
915 if( processShell( subShape, data, pptr, &itemList, lcolor ) )
916 ret = true;
917 }
918 else
919 {
920 wxLogTrace( MASK_OCE, wxT( "Unsupported subshape in solid" ) );
921 }
922 }
923 }
924
925 if( !ret )
926 childNode.Destroy();
927 else if( nullptr != items )
928 items->push_back( pptr );
929
930 return ret;
931}
932
933
934bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
935 std::vector<SGNODE*>* aItems )
936{
937 std::string labelTag;
938
939 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
940 {
941 // can be expensive, guard it if we aren't logging
942 getTag( aLabel, labelTag );
943 }
944
945 wxLogTrace( MASK_OCE, wxT( "Processing label %s" ), labelTag );
946
947 TopoDS_Shape originalShape;
948 TDF_Label shapeLabel = aLabel;
949
950 if( !aData.m_assy->GetShape( shapeLabel, originalShape ) )
951 {
952 return false;
953 }
954
955 TopoDS_Shape shape = originalShape;
956
957 if( aData.m_assy->IsReference( aLabel ) )
958 {
959 wxLogTrace( MASK_OCE, wxT( "Label %s is ref, trying to pull up referred label" ),
960 labelTag );
961
962 if( !aData.m_assy->GetReferredShape( aLabel, shapeLabel ) )
963 {
964 return false;
965 }
966
967 labelTag = static_cast<int>( shapeLabel.Tag() );
968 // wxLogTrace( MASK_OCE, wxT( "Label %s referred" ), labelTag );
969
970 if( !aData.m_assy->GetShape( shapeLabel, shape ) )
971 {
972 return false;
973 }
974 }
975
976 // Now let's see if the original label has a location
977 // Labels can be used to place copies of other labels at a specific location
978 IFSG_TRANSFORM childNode( aParent );
979 SGNODE* pptr = childNode.GetRawPtr();
980 const TopLoc_Location& loc = originalShape.Location();
981
982 if( !loc.IsIdentity() )
983 {
984 wxLogTrace( MASK_OCE, wxT( "Label %d has location" ), static_cast<int>( aLabel.Tag() ) );
985 gp_Trsf T = loc.Transformation();
986 gp_XYZ coord = T.TranslationPart();
987 childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
988 wxLogTrace( MASK_OCE, wxT( "Translation %f, %f, %f" ), coord.X(), coord.Y(), coord.Z() );
989 gp_XYZ axis;
990 Standard_Real angle;
991
992 if( T.GetRotation( axis, angle ) )
993 {
994 childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
995 wxLogTrace( MASK_OCE, wxT( "Rotation %f, %f, %f, angle %f" ),
996 axis.X(), axis.Y(), axis.Z(), angle );
997 }
998 }
999
1000 TopAbs_ShapeEnum stype = shape.ShapeType();
1001 bool ret = false;
1002 aData.hasSolid = false;
1003
1004 switch( stype )
1005 {
1006 case TopAbs_COMPOUND:
1007 {
1008 // assemblies will report a shape type of compound which isn't what we are after
1009 // we will still process the children of assemblies but they should just be label references to the actual shapes
1010 if( !aData.m_assy->IsAssembly( shapeLabel ) )
1011 {
1012 TopExp_Explorer xp;
1013
1014 for( xp.Init( shape, TopAbs_SOLID ); xp.More(); xp.Next() )
1015 {
1016 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1017 ret = true;
1018 }
1019
1020 // Get all shells, avoid those that may be attached to solids
1021 for( xp.Init( shape, TopAbs_SHELL, TopAbs_SOLID ); xp.More(); xp.Next() )
1022 {
1023 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1024 ret = true;
1025 }
1026
1027 // Get all faces, avoid those that may be attached to shells
1028 for( xp.Init( shape, TopAbs_FACE, TopAbs_SHELL ); xp.More(); xp.Next() )
1029 {
1030 const TopoDS_Face& face = TopoDS::Face( xp.Current() );
1031 processFace( face, aData, pptr, aItems, nullptr );
1032 ret = true;
1033 }
1034 }
1035 }
1036 break;
1037
1038 case TopAbs_SOLID:
1039 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1040 ret = true;
1041
1042 break;
1043
1044 case TopAbs_SHELL:
1045 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1046 ret = true;
1047
1048 break;
1049
1050 case TopAbs_FACE:
1051 if( processFace( TopoDS::Face( shape ), aData, pptr, aItems, nullptr ) )
1052 ret = true;
1053
1054 break;
1055
1056 default:
1057 break;
1058 }
1059
1060 if( nullptr != aItems )
1061 aItems->push_back( pptr );
1062
1063 if( !aData.m_assy->IsSimpleShape( shapeLabel ) && shapeLabel.HasChild() )
1064 {
1065 wxLogTrace( MASK_OCE, wxT( "Label %s has children" ), labelTag );
1066 TDF_ChildIterator it;
1067
1068 for( it.Initialize( shapeLabel ); it.More(); it.Next() )
1069 {
1070 if( processLabel( it.Value(), aData, pptr, aItems ) )
1071 ret = true;
1072 }
1073 }
1074
1075 return ret;
1076}
1077
1078
1079bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, std::vector<SGNODE*>* items,
1080 Quantity_ColorRGBA* color )
1081{
1082 if( Standard_True == face.IsNull() )
1083 return false;
1084
1085 bool reverse = ( face.Orientation() == TopAbs_REVERSED );
1086 SGNODE* ashape = nullptr;
1087 std::string partID;
1088 TDF_Label label;
1089
1090 bool useBothSides = false;
1091
1092 // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant
1093 // of a SOLID then hasSolid = false and we must render both sides
1094 if( data.renderBoth || !data.hasSolid )
1095 useBothSides = true;
1096
1097 if( data.m_assy->FindShape( face, label, Standard_False ) )
1098 getTag( label, partID );
1099
1100 if( !partID.empty() )
1101 ashape = data.GetFace( partID );
1102
1103 if( ashape )
1104 {
1105 if( nullptr == S3D::GetSGNodeParent( ashape ) )
1106 S3D::AddSGNodeChild( parent, ashape );
1107 else
1108 S3D::AddSGNodeRef( parent, ashape );
1109
1110 if( nullptr != items )
1111 items->push_back( ashape );
1112
1113 if( useBothSides )
1114 {
1115 std::string id2 = partID;
1116 id2.append( "b" );
1117 SGNODE* shapeB = data.GetFace( id2 );
1118
1119 if( nullptr == S3D::GetSGNodeParent( shapeB ) )
1120 S3D::AddSGNodeChild( parent, shapeB );
1121 else
1122 S3D::AddSGNodeRef( parent, shapeB );
1123
1124 if( nullptr != items )
1125 items->push_back( shapeB );
1126 }
1127
1128 return true;
1129 }
1130
1131 TopLoc_Location loc;
1132 Standard_Boolean isTessellate (Standard_False);
1133 Handle( Poly_Triangulation ) triangulation = BRep_Tool::Triangulation( face, loc );
1134 const double linDeflection = ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection;
1135
1136 if( triangulation.IsNull() || triangulation->Deflection() > linDeflection + Precision::Confusion() )
1137 isTessellate = Standard_True;
1138
1139 if( isTessellate )
1140 {
1141 BRepMesh_IncrementalMesh IM(face, linDeflection, Standard_False,
1142 glm::radians(ADVANCED_CFG::GetCfg().m_OcePluginAngularDeflection) );
1143 triangulation = BRep_Tool::Triangulation( face, loc );
1144 }
1145
1146 if( triangulation.IsNull() == Standard_True )
1147 return false;
1148
1149 Quantity_ColorRGBA lcolor;
1150
1151 // check for a face color; this has precedence over SOLID colors
1152 if( data.m_color->GetColor( face, XCAFDoc_ColorGen, lcolor )
1153 || data.m_color->GetColor( face, XCAFDoc_ColorCurv, lcolor )
1154 || data.m_color->GetColor( face, XCAFDoc_ColorSurf, lcolor ) )
1155 color = &lcolor;
1156
1157 SGNODE* ocolor = data.GetColor( color );
1158
1159 // create a SHAPE and attach the color and data,
1160 // then attach the shape to the parent and return TRUE
1161 IFSG_SHAPE vshape( true );
1162 IFSG_FACESET vface( vshape );
1163 IFSG_COORDS vcoords( vface );
1164 IFSG_COORDINDEX coordIdx( vface );
1165
1166 if( nullptr == S3D::GetSGNodeParent( ocolor ) )
1167 S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor );
1168 else
1169 S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor );
1170
1171 std::vector< SGPOINT > vertices;
1172 std::vector< int > indices;
1173 std::vector< int > indices2;
1174 gp_Trsf tx;
1175
1176 for( int i = 1; i <= triangulation->NbNodes(); i++ )
1177 {
1178 gp_XYZ v( triangulation->Node(i).Coord() );
1179 vertices.emplace_back( v.X(), v.Y(), v.Z() );
1180 }
1181
1182 for( int i = 1; i <= triangulation->NbTriangles(); i++ )
1183 {
1184 int a, b, c;
1185 triangulation->Triangle(i).Get(a, b, c);
1186 a--;
1187
1188 if( reverse )
1189 {
1190 int tmp = b - 1;
1191 b = c - 1;
1192 c = tmp;
1193 }
1194 else
1195 {
1196 b--;
1197 c--;
1198 }
1199
1200 indices.push_back( a );
1201 indices.push_back( b );
1202 indices.push_back( c );
1203
1204 if( useBothSides )
1205 {
1206 indices2.push_back( b );
1207 indices2.push_back( a );
1208 indices2.push_back( c );
1209 }
1210 }
1211
1212 vcoords.SetCoordsList( vertices.size(), &vertices[0] );
1213 coordIdx.SetIndices( indices.size(), &indices[0] );
1214 vface.CalcNormals( nullptr );
1215 vshape.SetParent( parent );
1216
1217 if( !partID.empty() )
1218 data.faces.emplace( partID, vshape.GetRawPtr() );
1219
1220 // The outer surface of an IGES model is indeterminate so
1221 // we must render both sides of a surface.
1222 if( useBothSides )
1223 {
1224 std::string id2 = partID;
1225 id2.append( "b" );
1226 IFSG_SHAPE vshape2( true );
1227 IFSG_FACESET vface2( vshape2 );
1228 IFSG_COORDS vcoords2( vface2 );
1229 IFSG_COORDINDEX coordIdx2( vface2 );
1230 S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor );
1231
1232 vcoords2.SetCoordsList( vertices.size(), &vertices[0] );
1233 coordIdx2.SetIndices( indices2.size(), &indices2[0] );
1234 vface2.CalcNormals( nullptr );
1235 vshape2.SetParent( parent );
1236
1237 if( !partID.empty() )
1238 data.faces.emplace( id2, vshape2.GetRawPtr() );
1239 }
1240
1241 return true;
1242}
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
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:790
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:1079
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:486
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:396
#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:331
void addItems(SGNODE *parent, std::vector< SGNODE * > *lp)
Definition: loadmodel.cpp:507
std::string getShapeName(TopAbs_ShapeEnum aShape)
Gets a string for a given TopAbs_ShapeEnum element.
Definition: loadmodel.cpp:378
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:422
SCENEGRAPH * LoadModel(char const *filename)
Definition: loadmodel.cpp:683
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:475
FormatType fileType(const char *aFileName)
Definition: loadmodel.cpp:284
bool readSTEP(Handle(TDocStd_Document)&m_doc, const char *fname)
Definition: loadmodel.cpp:569
static std::ostream & operator<<(std::ostream &aOStream, const Quantity_ColorRGBA &aColor)
Definition: loadmodel.cpp:402
#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:809
static wxString getLabelName(const TDF_Label &aLabel)
Definition: loadmodel.cpp:355
bool processLabel(const TDF_Label &aLabel, DATA &aData, SGNODE *aParent, std::vector< SGNODE * > *aItems)
Definition: loadmodel.cpp:934
std::map< std::string, SGNODE * > FACEMAP
Definition: loadmodel.cpp:93
bool readSTEPZ(Handle(TDocStd_Document)&m_doc, const char *aFileName)
Definition: loadmodel.cpp:613
bool readIGES(Handle(TDocStd_Document) &m_doc, const char *fname)
Definition: loadmodel.cpp:530
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