5 changed files with 1131 additions and 0 deletions
-
11CMakeLists.txt
-
4plugins/3d/CMakeLists.txt
-
37plugins/3d/oce/CMakeLists.txt
-
912plugins/3d/oce/loadmodel.cpp
-
167plugins/3d/oce/oce.cpp
@ -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 ); |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue