Added 3D plugin for OCE; this plugin is activated by -DUSE_OCE=ON
This commit is contained in:
parent
7e3aedda82
commit
b656a8180a
|
@ -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 path>/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.
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
add_subdirectory( idf )
|
||||
add_subdirectory( vrml )
|
||||
|
||||
if( USE_OCE )
|
||||
add_subdirectory( oce )
|
||||
endif( USE_OCE )
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -0,0 +1,912 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
*
|
||||
* 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 <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
|
||||
#include <wx/filename.h>
|
||||
#include <wx/string.h>
|
||||
#endif
|
||||
|
||||
#include <TDocStd_Document.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <Quantity_Color.hxx>
|
||||
#include <XCAFApp_Application.hxx>
|
||||
#include <Handle_XCAFApp_Application.hxx>
|
||||
|
||||
#include <AIS_Shape.hxx>
|
||||
|
||||
#include <IGESControl_Reader.hxx>
|
||||
#include <IGESCAFControl_Reader.hxx>
|
||||
#include <Interface_Static.hxx>
|
||||
|
||||
#include <STEPControl_Reader.hxx>
|
||||
#include <STEPCAFControl_Reader.hxx>
|
||||
|
||||
#include <XCAFDoc_DocumentTool.hxx>
|
||||
#include <XCAFDoc_ColorTool.hxx>
|
||||
#include <Handle_XCAFDoc_ColorTool.hxx>
|
||||
#include <XCAFDoc_ShapeTool.hxx>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
|
||||
#include <Quantity_Color.hxx>
|
||||
#include <Poly_Triangulation.hxx>
|
||||
#include <Poly_PolygonOnTriangulation.hxx>
|
||||
#include <Precision.hxx>
|
||||
|
||||
#include <TDF_LabelSequence.hxx>
|
||||
#include <TDF_ChildIterator.hxx>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
*
|
||||
* 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 <wx/filename.h>
|
||||
#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 );
|
||||
}
|
Loading…
Reference in New Issue