diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b4b2741ea..3b3ba64545 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,10 @@ option( USE_SCH_IO_MANAGER "Build Eeschema with the I/O manager for handling schematic and symbol library I/O. (default OFF)" ) +option( USE_OCE + "Build tools and plugins related to OpenCascade Community Edition (default OFF)" + ) + # when option KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES is enabled: # PYTHON_EXECUTABLE can be defined when invoking cmake # ( use -DPYTHON_EXECUTABLE=/python.exe or python2 ) @@ -507,6 +511,13 @@ if( KICAD_SPICE ) find_package( ngspice REQUIRED ) endif() +# Find OpenCascade Community Edition, required for STEP plugin and tools +if( USE_OCE ) + set( LIBS_OCE TKBinXCAF TKPCAF TKSTEP TKXDESTEP TKIGES TKXDEIGES ) + + find_package( OCE 0.16 REQUIRED ${LIBS_OCE} ) +endif() + # Assist with header file searching optimization: # INC_BEFORE and INC_AFTER are two lists which go at the front and back of the # header file search lists, respectively. diff --git a/plugins/3d/CMakeLists.txt b/plugins/3d/CMakeLists.txt index 2564ef093f..5cd00fefe9 100644 --- a/plugins/3d/CMakeLists.txt +++ b/plugins/3d/CMakeLists.txt @@ -1,2 +1,6 @@ add_subdirectory( idf ) add_subdirectory( vrml ) + +if( USE_OCE ) + add_subdirectory( oce ) +endif( USE_OCE ) diff --git a/plugins/3d/oce/CMakeLists.txt b/plugins/3d/oce/CMakeLists.txt new file mode 100644 index 0000000000..067dc1762e --- /dev/null +++ b/plugins/3d/oce/CMakeLists.txt @@ -0,0 +1,37 @@ +# Developers may wish to set DEBUG_OCE to a value from 1..3 to +# enable informational messages in Debug build. Setting a value +# of 4 or greater will instruct the parser to write a VRML2 +# equivalent copy of the input file by invoking the +# SceneGraph->WriteVRML() function and depending on the setup +# the object may attempt to write to a protected directory. +# +# In addition to setting the verbosity via DEBUG_OCE, an +# appropriate WXTRACE value must be set prior to program +# execution to enable the logging: +# +# export WXTRACE="PLUGIN_OCE" +# + +include_directories( + ${OCE_INCLUDE_DIRS} +) + +add_library( s3d_plugin_oce MODULE + oce.cpp + loadmodel.cpp + ) + +target_link_libraries( s3d_plugin_oce kicad_3dsg ${LIBS_OCE} ${wxWidgets_LIBRARIES} ) + +if( APPLE ) + # puts library into the main kicad.app bundle in build tree + set_target_properties( s3d_plugin_oce PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${OSX_BUNDLE_BUILD_PLUGIN_DIR}/3d" + ) +endif() + +install( TARGETS + s3d_plugin_oce + DESTINATION ${KICAD_USER_PLUGIN}/3d + COMPONENT binary + ) diff --git a/plugins/3d/oce/loadmodel.cpp b/plugins/3d/oce/loadmodel.cpp new file mode 100644 index 0000000000..720fa68fd4 --- /dev/null +++ b/plugins/3d/oce/loadmodel.cpp @@ -0,0 +1,912 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 ) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "plugins/3dapi/ifsg_all.h" + +// log mask for wxLogTrace +#define MASK_OCE "PLUGIN_OCE" + +// precision for mesh creation; 0.07 should be good enough for ECAD viewing +#define USER_PREC (0.14) +// angular deflection for meshing +// 10 deg (36 faces per circle) = 0.17453293 +// 20 deg (18 faces per circle) = 0.34906585 +// 30 deg (12 faces per circle) = 0.52359878 +#define USER_ANGLE (0.52359878) + +typedef std::map< Standard_Real, SGNODE* > COLORMAP; +typedef std::map< std::string, SGNODE* > FACEMAP; +typedef std::map< std::string, std::vector< SGNODE* > > NODEMAP; +typedef std::pair< std::string, std::vector< SGNODE* > > NODEITEM; + +struct DATA; + +bool processNode( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items ); + +bool processComp( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items ); + +bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items, Quantity_Color* color ); + +struct DATA +{ + Handle( TDocStd_Document ) m_doc; + Handle( XCAFDoc_ColorTool ) m_color; + Handle( XCAFDoc_ShapeTool ) m_assy; + SGNODE* scene; + SGNODE* defaultColor; + Quantity_Color refColor; + NODEMAP shapes; // SGNODE lists representing a TopoDS_SOLID / COMPOUND + COLORMAP colors; // SGAPPEARANCE nodes + FACEMAP faces; // SGSHAPE items representing a TopoDS_FACE + bool renderBoth; // set TRUE if we're processing IGES + bool hasSolid; // set TRUE if there is no parent SOLID + + DATA() + { + scene = NULL; + defaultColor = NULL; + refColor.SetValues( Quantity_NOC_BLACK ); + renderBoth = false; + hasSolid = false; + } + + ~DATA() + { + // destroy any colors with no parent + if( !colors.empty() ) + { + COLORMAP::iterator sC = colors.begin(); + COLORMAP::iterator eC = colors.end(); + + while( sC != eC ) + { + if( NULL == S3D::GetSGNodeParent( sC->second ) ) + S3D::DestroyNode( sC->second ); + + ++sC; + } + + colors.clear(); + } + + if( defaultColor && NULL == S3D::GetSGNodeParent( defaultColor ) ) + S3D::DestroyNode(defaultColor); + + // destroy any faces with no parent + if( !faces.empty() ) + { + FACEMAP::iterator sF = faces.begin(); + FACEMAP::iterator eF = faces.end(); + + while( sF != eF ) + { + if( NULL == S3D::GetSGNodeParent( sF->second ) ) + S3D::DestroyNode( sF->second ); + + ++sF; + } + + faces.clear(); + } + + // destroy any shapes with no parent + if( !shapes.empty() ) + { + NODEMAP::iterator sS = shapes.begin(); + NODEMAP::iterator eS = shapes.end(); + + while( sS != eS ) + { + std::vector< SGNODE* >::iterator sV = sS->second.begin(); + std::vector< SGNODE* >::iterator eV = sS->second.end(); + + while( sV != eV ) + { + if( NULL == S3D::GetSGNodeParent( *sV ) ) + S3D::DestroyNode( *sV ); + + ++sV; + } + + sS->second.clear(); + ++sS; + } + + shapes.clear(); + } + + if( scene ) + S3D::DestroyNode(scene); + + return; + } + + // find collection of tagged nodes + bool GetShape( const std::string& id, std::vector< SGNODE* >*& listPtr ) + { + listPtr = NULL; + NODEMAP::iterator item; + item = shapes.find( id ); + + if( item == shapes.end() ) + return false; + + listPtr = &item->second; + return true; + } + + // find collection of tagged nodes + SGNODE* GetFace( const std::string& id ) + { + FACEMAP::iterator item; + item = faces.find( id ); + + if( item == faces.end() ) + return NULL; + + return item->second; + } + + // return color if found; if not found, create SGAPPEARANCE + SGNODE* GetColor( Quantity_Color* colorObj ) + { + if( NULL == colorObj ) + { + if( defaultColor ) + return defaultColor; + + IFSG_APPEARANCE app( true ); + app.SetShininess( 0.05 ); + app.SetSpecular( 0.04, 0.04, 0.04 ); + app.SetAmbient( 0.1, 0.1, 0.1 ); + app.SetDiffuse( 0.6,0.6, 0.6 ); + + defaultColor = app.GetRawPtr(); + return defaultColor; + } + + Standard_Real id = colorObj->Distance( refColor ); + std::map< Standard_Real, SGNODE* >::iterator item; + item = colors.find( id ); + + if( item != colors.end() ) + return item->second; + + IFSG_APPEARANCE app( true ); + app.SetShininess( 0.1 ); + app.SetSpecular( 0.12, 0.12, 0.12 ); + app.SetAmbient( 0.1, 0.1, 0.1 ); + app.SetDiffuse( colorObj->Red(), colorObj->Green(), colorObj->Blue() ); + colors.insert( std::pair< Standard_Real, SGNODE* >( id, app.GetRawPtr() ) ); + + return app.GetRawPtr(); + } +}; + + +enum FormatType +{ + FMT_NONE = 0, + FMT_STEP = 1, + FMT_IGES = 2 +}; + + +FormatType fileType( const char* aFileName ) +{ + std::ifstream ifile; + ifile.open( aFileName ); + + if( !ifile.is_open() ) + return FMT_NONE; + + char iline[82]; + memset( iline, 0, 82 ); + ifile.getline( iline, 82 ); + ifile.close(); + iline[81] = 0; // ensure NULL termination when string is too long + + // check for STEP in Part 21 format + // (this can give false positives since Part 21 is not exclusively STEP) + if( !strncmp( iline, "ISO-10303-21;", 13 ) ) + return FMT_STEP; + + std::string fstr = iline; + + // check for STEP in XML format + // (this can give both false positive and false negatives) + if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos ) + return FMT_STEP; + + // Note: this is a very simple test which can yield false positives; the only + // sure method for determining if a file *not* an IGES model is to attempt + // to load it. + if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) ) + return FMT_IGES; + + return FMT_NONE; +} + + +void getTag( TDF_Label& label, std::string& aTag ) +{ + aTag.clear(); + + if( label.IsNull() ) + return; + + std::string rtag; // tag in reverse + aTag.clear(); + int id = label.Tag(); + std::ostringstream ostr; + ostr << id; + rtag = ostr.str(); + ostr.str( "" ); + ostr.clear(); + + TDF_Label nlab = label.Father(); + + while( !nlab.IsNull() ) + { + rtag.append( 1, ':' ); + id = nlab.Tag(); + ostr << id; + rtag.append( ostr.str() ); + ostr.str( "" ); + ostr.clear(); + nlab = nlab.Father(); + }; + + std::string::reverse_iterator bI = rtag.rbegin(); + std::string::reverse_iterator eI = rtag.rend(); + + while( bI != eI ) + { + aTag.append( 1, *bI ); + ++bI; + } + + return; +} + + +bool getColor( DATA& data, TDF_Label label, Quantity_Color& color ) +{ + while( true ) + { + if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) ) + return true; + else if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) ) + return true; + else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) ) + return true; + + label = label.Father(); + + if( label.IsNull() ) + break; + }; + + return false; +} + + +void addItems( SGNODE* parent, std::vector< SGNODE* >* lp ) +{ + if( NULL == lp ) + return; + + std::vector< SGNODE* >::iterator sL = lp->begin(); + std::vector< SGNODE* >::iterator eL = lp->end(); + SGNODE* item; + + while( sL != eL ) + { + item = *sL; + + if( NULL == S3D::GetSGNodeParent( item ) ) + S3D::AddSGNodeChild( parent, item ); + else + S3D::AddSGNodeRef( parent, item ); + + ++sL; + } + + return; +} + + +bool readIGES( Handle(TDocStd_Document)& m_doc, const char* fname ) +{ + IGESCAFControl_Reader reader; + IFSelect_ReturnStatus stat = reader.ReadFile( fname ); + reader.PrintCheckLoad( Standard_False, IFSelect_ItemsByEntity ); + + if( stat != IFSelect_RetDone ) + return false; + + // Enable user-defined shape precision + if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) ) + return false; + + // Set the shape conversion precision to USER_PREC (default 0.0001 has too many triangles) + if( !Interface_Static::SetRVal( "read.precision.val", USER_PREC ) ) + return false; + + // set other translation options + reader.SetColorMode(true); // use model colors + reader.SetNameMode(false); // don't use IGES label names + reader.SetLayerMode(false); // ignore LAYER data + + if ( !reader.Transfer( m_doc ) ) + return false; + + // are there any shapes to translate? + if( reader.NbShapes() < 1 ) + return false; + + return true; +} + + +bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname ) +{ + STEPCAFControl_Reader reader; + IFSelect_ReturnStatus stat = reader.ReadFile( fname ); + + if( stat != IFSelect_RetDone ) + return false; + + // Enable user-defined shape precision + if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) ) + return false; + + // Set the shape conversion precision to USER_PREC (default 0.0001 has too many triangles) + if( !Interface_Static::SetRVal( "read.precision.val", USER_PREC ) ) + return false; + + // set other translation options + reader.SetColorMode(true); // use model colors + reader.SetNameMode(false); // don't use label names + reader.SetLayerMode(false); // ignore LAYER data + + if ( !reader.Transfer( m_doc ) ) + { + m_doc->Close(); + return false; + } + + // are there any shapes to translate? + if( reader.NbRootsForTransfer() < 1 ) + return false; + + return true; +} + + +SCENEGRAPH* LoadModel( char const* filename ) +{ + DATA data; + + Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication(); + m_app->NewDocument( "MDTV-XCAF", data.m_doc ); + FormatType modelFmt = fileType( filename ); + + switch( modelFmt ) + { + case FMT_IGES: + data.renderBoth = true; + + if( !readIGES( data.m_doc, filename ) ) + return NULL; + break; + + case FMT_STEP: + if( !readSTEP( data.m_doc, filename ) ) + return NULL; + break; + + default: + return NULL; + break; + } + + data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() ); + data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() ); + + // retrieve all free shapes + TDF_LabelSequence frshapes; + data.m_assy->GetFreeShapes( frshapes ); + + int nshapes = frshapes.Length(); + int id = 1; + bool ret = false; + + // create the top level SG node + IFSG_TRANSFORM topNode( true ); + data.scene = topNode.GetRawPtr(); + + while( id <= nshapes ) + { + TopoDS_Shape shape = data.m_assy->GetShape( frshapes.Value(id) ); + + if ( !shape.IsNull() && processNode( shape, data, data.scene, NULL ) ) + ret = true; + + ++id; + }; + + if( !ret ) + return NULL; + + SCENEGRAPH* scene = (SCENEGRAPH*)data.scene; + + // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE + #if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 ) + if( data.scene ) + { + wxFileName fn( wxString::FromUTF8Unchecked( filename ) ); + wxString output; + + if( FMT_STEP == modelFmt ) + output = wxT( "_step-" ); + else + output = wxT( "_iges-" ); + + output.append( fn.GetName() ); + output.append( wxT(".wrl") ); + S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true ); + } + #endif + + // set to NULL to prevent automatic destruction of the scene data + data.scene = NULL; + + return scene; +} + + +bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items, Quantity_Color* color ) +{ + TopoDS_Iterator it; + bool ret = false; + + for( it.Initialize( shape, false, false ); it.More(); it.Next() ) + { + const TopoDS_Face& face = TopoDS::Face( it.Value() ); + + if( processFace( face, data, parent, items, color ) ) + ret = true; + } + + return ret; +} + + +bool processSolid( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items ) +{ + TDF_Label label = data.m_assy->FindShape( shape, Standard_False ); + + data.hasSolid = true; + std::string partID; + Quantity_Color col; + Quantity_Color* lcolor = NULL; + + if( label.IsNull() ) + { + static int i = 0; + std::ostringstream ostr; + ostr << "KMISC_" << i++; + partID = ostr.str(); + } + else + { + getTag( label, partID ); + + + if( getColor( data, label, col ) ) + lcolor = &col; + } + + TopoDS_Iterator it; + IFSG_TRANSFORM childNode( parent ); + SGNODE* pptr = childNode.GetRawPtr(); + TopLoc_Location loc = shape.Location(); + bool ret = false; + + if( !loc.IsIdentity() ) + { + gp_Trsf T = loc.Transformation(); + gp_XYZ coord = T.TranslationPart(); + childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) ); + gp_XYZ axis; + Standard_Real angle; + + if( T.GetRotation( axis, angle ) ) + childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle ); + } + + std::vector< SGNODE* >* component = NULL; + + if( !partID.empty() ) + data.GetShape( partID, component ); + + if( component ) + { + addItems( pptr, component ); + + if( NULL != items ) + items->push_back( pptr ); + } + + // instantiate the solid + std::vector< SGNODE* > itemList; + + for( it.Initialize( shape, false, false ); it.More(); it.Next() ) + { + const TopoDS_Shape& subShape = it.Value(); + + if( processShell( subShape, data, pptr, &itemList, lcolor ) ) + ret = true; + } + + if( !ret ) + childNode.Destroy(); + else if( NULL != items ) + items->push_back( pptr ); + + return ret; +} + + +bool processComp( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items ) +{ + TopoDS_Iterator it; + IFSG_TRANSFORM childNode( parent ); + SGNODE* pptr = childNode.GetRawPtr(); + TopLoc_Location loc = shape.Location(); + bool ret = false; + + if( !loc.IsIdentity() ) + { + gp_Trsf T = loc.Transformation(); + gp_XYZ coord = T.TranslationPart(); + childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) ); + gp_XYZ axis; + Standard_Real angle; + + if( T.GetRotation( axis, angle ) ) + childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle ); + } + + for( it.Initialize( shape, false, false ); it.More(); it.Next() ) + { + const TopoDS_Shape& subShape = it.Value(); + TopAbs_ShapeEnum stype = subShape.ShapeType(); + data.hasSolid = false; + + switch( stype ) + { + case TopAbs_COMPOUND: + case TopAbs_COMPSOLID: + if( processComp( subShape, data, pptr, items ) ) + ret = true; + break; + + case TopAbs_SOLID: + if( processSolid( subShape, data, pptr, items ) ) + ret = true; + break; + + case TopAbs_SHELL: + if( processShell( subShape, data, pptr, items, NULL ) ) + ret = true; + break; + + case TopAbs_FACE: + if( processFace( TopoDS::Face( subShape ), data, pptr, items, NULL ) ) + ret = true; + break; + + default: + break; + } + } + + if( !ret ) + childNode.Destroy(); + else if( NULL != items ) + items->push_back( pptr ); + + return ret; +} + + +bool processNode( const TopoDS_Shape& shape, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items ) +{ + TopAbs_ShapeEnum stype = shape.ShapeType(); + bool ret = false; + data.hasSolid = false; + + switch( stype ) + { + case TopAbs_COMPOUND: + case TopAbs_COMPSOLID: + if( processComp( shape, data, parent, items ) ) + ret = true; + break; + + case TopAbs_SOLID: + if( processSolid( shape, data, parent, items ) ) + ret = true; + break; + + case TopAbs_SHELL: + if( processShell( shape, data, parent, items, NULL ) ) + ret = true; + break; + + case TopAbs_FACE: + if( processFace( TopoDS::Face( shape ), data, parent, items, NULL ) ) + ret = true; + break; + + default: + break; + } + + return ret; +} + + +bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, + std::vector< SGNODE* >* items, Quantity_Color* color ) +{ + if( Standard_True == face.IsNull() ) + return false; + + bool reverse = ( face.Orientation() == TopAbs_REVERSED ); + SGNODE* ashape = NULL; + std::string partID; + TDF_Label label; + + bool useBothSides = false; + + // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant + // of a SOLID then hasSolid = false and we must render both sides + if( data.renderBoth || !data.hasSolid ) + useBothSides = true; + + if( data.m_assy->FindShape( face, label, Standard_False ) ) + getTag( label, partID ); + + if( !partID.empty() ) + ashape = data.GetFace( partID ); + + if( ashape ) + { + if( NULL == S3D::GetSGNodeParent( ashape ) ) + S3D::AddSGNodeChild( parent, ashape ); + else + S3D::AddSGNodeRef( parent, ashape ); + + if( NULL != items ) + items->push_back( ashape ); + + if( useBothSides ) + { + std::string id2 = partID; + id2.append( "b" ); + SGNODE* shapeB = data.GetFace( id2 ); + + if( NULL == S3D::GetSGNodeParent( shapeB ) ) + S3D::AddSGNodeChild( parent, shapeB ); + else + S3D::AddSGNodeRef( parent, shapeB ); + + if( NULL != items ) + items->push_back( shapeB ); + } + + return true; + } + + TopLoc_Location loc; + Standard_Boolean isTessellate (Standard_False); + Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation( face, loc ); + + if( triangulation.IsNull() || triangulation->Deflection() > USER_PREC + Precision::Confusion() ) + isTessellate = Standard_True; + + if (isTessellate) + { + BRepMesh_IncrementalMesh IM(face, USER_PREC, Standard_False, USER_ANGLE ); + triangulation = BRep_Tool::Triangulation( face, loc ); + } + + if( triangulation.IsNull() == Standard_True ) + return false; + + Quantity_Color lcolor; + + // check for a face color; this has precedence over SOLID colors + do + { + TDF_Label L; + + if( data.m_color->ShapeTool()->Search( face, L ) ) + { + if( data.m_color->GetColor( L, XCAFDoc_ColorGen, lcolor ) + || data.m_color->GetColor( L, XCAFDoc_ColorCurv, lcolor ) + || data.m_color->GetColor( L, XCAFDoc_ColorSurf, lcolor ) ) + color = &lcolor; + } + } while( 0 ); + + SGNODE* ocolor = data.GetColor( color ); + + // create a SHAPE and attach the color and data, + // then attach the shape to the parent and return TRUE + IFSG_SHAPE vshape( true ); + IFSG_FACESET vface( vshape ); + IFSG_COORDS vcoords( vface ); + IFSG_COORDINDEX coordIdx( vface ); + + if( NULL == S3D::GetSGNodeParent( ocolor ) ) + S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor ); + else + S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor ); + + const TColgp_Array1OfPnt& arrPolyNodes = triangulation->Nodes(); + const Poly_Array1OfTriangle& arrTriangles = triangulation->Triangles(); + + std::vector< SGPOINT > vertices; + std::vector< int > indices; + std::vector< int > indices2; + gp_Trsf tx; + + for(int i = 1; i <= triangulation->NbNodes(); i++) + { + gp_XYZ v( arrPolyNodes(i).Coord() ); + vertices.push_back( SGPOINT( v.X(), v.Y(), v.Z() ) ); + } + + for(int i = 1; i <= triangulation->NbTriangles(); i++) + { + int a, b, c; + arrTriangles( i ).Get( a, b, c ); + a--; + + if( reverse ) + { + int tmp = b - 1; + b = c - 1; + c = tmp; + } else { + b--; + c--; + } + + indices.push_back( a ); + indices.push_back( b ); + indices.push_back( c ); + + if( useBothSides ) + { + indices2.push_back( b ); + indices2.push_back( a ); + indices2.push_back( c ); + } + } + + vcoords.SetCoordsList( vertices.size(), &vertices[0] ); + coordIdx.SetIndices( indices.size(), &indices[0] ); + vface.CalcNormals( NULL ); + vshape.SetParent( parent ); + + if( !partID.empty() ) + data.faces.insert( std::pair< std::string, + SGNODE* >( partID, vshape.GetRawPtr() ) ); + + // The outer surface of an IGES model is indeterminate so + // we must render both sides of a surface. + if( useBothSides ) + { + std::string id2 = partID; + id2.append( "b" ); + IFSG_SHAPE vshape2( true ); + IFSG_FACESET vface2( vshape2 ); + IFSG_COORDS vcoords2( vface2 ); + IFSG_COORDINDEX coordIdx2( vface2 ); + S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor ); + + vcoords2.SetCoordsList( vertices.size(), &vertices[0] ); + coordIdx2.SetIndices( indices2.size(), &indices2[0] ); + vface2.CalcNormals( NULL ); + vshape2.SetParent( parent ); + + if( !partID.empty() ) + data.faces.insert( std::pair< std::string, + SGNODE* >( id2, vshape2.GetRawPtr() ) ); + } + + return true; +} diff --git a/plugins/3d/oce/oce.cpp b/plugins/3d/oce/oce.cpp new file mode 100644 index 0000000000..62f6e82a6c --- /dev/null +++ b/plugins/3d/oce/oce.cpp @@ -0,0 +1,167 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * Description: + * This plugin implements a STEP/IGES model renderer for KiCad via OCE + */ + +#include +#include "plugins/3d/3d_plugin.h" +#include "plugins/3dapi/ifsg_all.h" + +SCENEGRAPH* LoadModel( char const* filename ); + +#define PLUGIN_OCE_MAJOR 1 +#define PLUGIN_OCE_MINOR 1 +#define PLUGIN_OCE_PATCH 1 +#define PLUGIN_OCE_REVNO 0 + + +const char* GetKicadPluginName( void ) +{ + return "PLUGIN_3D_OCE"; +} + + +void GetPluginVersion( unsigned char* Major, + unsigned char* Minor, unsigned char* Patch, unsigned char* Revision ) +{ + if( Major ) + *Major = PLUGIN_OCE_MAJOR; + + if( Minor ) + *Minor = PLUGIN_OCE_MINOR; + + if( Patch ) + *Patch = PLUGIN_OCE_PATCH; + + if( Revision ) + *Revision = PLUGIN_OCE_REVNO; + + return; +} + +// number of extensions supported +#ifdef _WIN32 +#define NEXTS 4 +#else +#define NEXTS 8 +#endif + +// number of filter sets supported +#define NFILS 2 + +static char ext0[] = "stp"; +static char ext1[] = "step"; +static char ext2[] = "igs"; +static char ext3[] = "iges"; + +#ifdef _WIN32 +static char fil0[] = "STEP (*.stp;*.step)|*.stp;*.step"; +static char fil1[] = "IGES (*.igs;*.iges)|*.igs;*.iges"; +#else +static char ext4[] = "STP"; +static char ext5[] = "STEP"; +static char ext6[] = "IGS"; +static char ext7[] = "IGES"; +static char fil0[] = "STEP (*.stp;*.STP;*.step;*.STEP)|*.stp;*.STP;*.step;*.STEP"; +static char fil1[] = "IGES (*.igs;*.IGS;*.iges;*.IGES)|*.igs;*.IGS;*.iges;*.IGES"; +#endif + +static struct FILE_DATA +{ + char const* extensions[NEXTS]; + char const* filters[NFILS]; + + FILE_DATA() + { + extensions[0] = ext0; + extensions[1] = ext1; + extensions[2] = ext2; + extensions[3] = ext3; + filters[0] = fil0; + filters[1] = fil1; + +#ifndef _WIN32 + extensions[4] = ext4; + extensions[5] = ext5; + extensions[6] = ext6; + extensions[7] = ext7; +#endif + + return; + } + +} file_data; + + +int GetNExtensions( void ) +{ + return NEXTS; +} + + +char const* GetModelExtension( int aIndex ) +{ + if( aIndex < 0 || aIndex >= NEXTS ) + return NULL; + + return file_data.extensions[aIndex]; +} + + +int GetNFilters( void ) +{ + return NFILS; +} + + +char const* GetFileFilter( int aIndex ) +{ + if( aIndex < 0 || aIndex >= NFILS ) + return NULL; + + return file_data.filters[aIndex]; +} + + +bool CanRender( void ) +{ + // this plugin supports rendering of IDF component outlines + return true; +} + + +SCENEGRAPH* Load( char const* aFileName ) +{ + if( NULL == aFileName ) + return NULL; + + wxString fname = wxString::FromUTF8Unchecked( aFileName ); + + if( !wxFileName::FileExists( fname ) ) + return NULL; + + return LoadModel( aFileName ); +}