diff --git a/3d-viewer/3d_cache/sg/ifsg_faceset.cpp b/3d-viewer/3d_cache/sg/ifsg_faceset.cpp index 144c3d14d3..69f86dc033 100644 --- a/3d-viewer/3d_cache/sg/ifsg_faceset.cpp +++ b/3d-viewer/3d_cache/sg/ifsg_faceset.cpp @@ -159,3 +159,12 @@ bool IFSG_FACESET::NewNode( IFSG_NODE& aParent ) return NewNode( np ); } + + +bool IFSG_FACESET::CalcNormals( void ) +{ + if( m_node ) + return ((SGFACESET*)m_node)->CalcNormals(); + + return false; +} diff --git a/3d-viewer/3d_cache/sg/sg_coordindex.cpp b/3d-viewer/3d_cache/sg/sg_coordindex.cpp index 2ca17ffb72..12bbeddc29 100644 --- a/3d-viewer/3d_cache/sg/sg_coordindex.cpp +++ b/3d-viewer/3d_cache/sg/sg_coordindex.cpp @@ -41,3 +41,14 @@ SGCOORDINDEX::~SGCOORDINDEX() { return; } + + +void SGCOORDINDEX::GatherCoordIndices( std::vector< int >& aIndexList ) +{ + if( index.empty() ) + return; + + aIndexList.insert( aIndexList.end(), index.begin(), index.end() ); + + return; +} diff --git a/3d-viewer/3d_cache/sg/sg_coordindex.h b/3d-viewer/3d_cache/sg/sg_coordindex.h index 6044b3b18a..2fc06ca77d 100644 --- a/3d-viewer/3d_cache/sg/sg_coordindex.h +++ b/3d-viewer/3d_cache/sg/sg_coordindex.h @@ -46,6 +46,13 @@ class SGCOORDINDEX : public SGINDEX public: SGCOORDINDEX( SGNODE* aParent ); virtual ~SGCOORDINDEX(); + + /** + * Function GatherCoordIndices + * adds all coordinate indices to the given list + * in preparation for a normals calculation + */ + void GatherCoordIndices( std::vector< int >& aIndexList ); }; #endif // SG_COORDINDEX_H diff --git a/3d-viewer/3d_cache/sg/sg_coords.cpp b/3d-viewer/3d_cache/sg/sg_coords.cpp index 9e0c6b1fe5..5aad9acf6f 100644 --- a/3d-viewer/3d_cache/sg/sg_coords.cpp +++ b/3d-viewer/3d_cache/sg/sg_coords.cpp @@ -24,6 +24,8 @@ #include #include "3d_cache/sg/sg_coords.h" #include "3d_cache/sg/sg_helpers.h" +#include "3d_cache/sg/sg_normals.h" +#include "3d_cache/sg/sg_faceset.h" SGCOORDS::SGCOORDS( SGNODE* aParent ) : SGNODE( aParent ) @@ -312,3 +314,37 @@ bool SGCOORDS::ReadCache( std::ifstream& aFile, SGNODE* parentNode ) return true; } + + +bool SGCOORDS::CalcNormals( void ) +{ + if( NULL == m_Parent ) + return false; + + // the parent and all references must have indices; collect all + // indices into one std::vector<> + std::vector< int > ilist; + ((SGFACESET*)m_Parent)->GatherCoordIndices( ilist ); + + std::list< SGNODE* >::iterator sB = m_BackPointers.begin(); + std::list< SGNODE* >::iterator eB = m_BackPointers.end(); + + while( sB != eB ) + { + SGFACESET* fp = (SGFACESET*)(*sB); + fp->GatherCoordIndices( ilist ); + ++sB; + } + + SGNORMALS* np = ((SGFACESET*)m_Parent)->m_Normals; + + if( !np ) + np = new SGNORMALS( m_Parent ); + + if( S3D::CalcTriangleNormals( coords, ilist, np->norms ) ) + return true; + + delete np; + + return false; +} diff --git a/3d-viewer/3d_cache/sg/sg_coords.h b/3d-viewer/3d_cache/sg/sg_coords.h index 2afc7db245..f8b05c10da 100644 --- a/3d-viewer/3d_cache/sg/sg_coords.h +++ b/3d-viewer/3d_cache/sg/sg_coords.h @@ -55,6 +55,13 @@ public: void AddCoord( double aXValue, double aYValue, double aZValue ); void AddCoord( const SGPOINT& aPoint ); + /** + * Function CalcNormals + * calculates normals for this coordinate list and sets the + * normals list in the parent SGFACESET + */ + bool CalcNormals( void ); + void ReNameNodes( void ); bool WriteVRML( std::ofstream& aFile, bool aReuseFlag ); diff --git a/3d-viewer/3d_cache/sg/sg_faceset.cpp b/3d-viewer/3d_cache/sg/sg_faceset.cpp index a99884cb6f..ca93eecd1b 100644 --- a/3d-viewer/3d_cache/sg/sg_faceset.cpp +++ b/3d-viewer/3d_cache/sg/sg_faceset.cpp @@ -1030,3 +1030,34 @@ bool SGFACESET::validate( void ) valid = true; return true; } + + +void SGFACESET::GatherCoordIndices( std::vector< int >& aIndexList ) +{ + if( m_CoordIndices ) + m_CoordIndices->GatherCoordIndices( aIndexList ); + + return; +} + + +bool SGFACESET::CalcNormals( void ) +{ + if( m_RCoords ) + { + SGFACESET* fp = (SGFACESET*) m_RCoords->GetParent(); + + if( !fp ) + return false; + + return fp->CalcNormals(); + } + + if( NULL == m_Coords || m_Coords->coords.empty() ) + return false; + + if( m_Normals && !m_Normals->norms.empty( ) ) + return true; + + return m_Coords->CalcNormals(); +} diff --git a/3d-viewer/3d_cache/sg/sg_faceset.h b/3d-viewer/3d_cache/sg/sg_faceset.h index 6cb3eee29d..acb0586104 100644 --- a/3d-viewer/3d_cache/sg/sg_faceset.h +++ b/3d-viewer/3d_cache/sg/sg_faceset.h @@ -76,11 +76,20 @@ public: bool AddRefNode( SGNODE* aNode ); bool AddChildNode( SGNODE* aNode ); + bool CalcNormals( void ); + void ReNameNodes( void ); bool WriteVRML( std::ofstream& aFile, bool aReuseFlag ); bool WriteCache( std::ofstream& aFile, SGNODE* parentNode ); bool ReadCache( std::ifstream& aFile, SGNODE* parentNode ); + + /** + * Function GatherCoordIndices + * adds all internal coordinate indices to the given list + * in preparation for a normals calculation + */ + void GatherCoordIndices( std::vector< int >& aIndexList ); }; /* diff --git a/3d-viewer/3d_cache/sg/sg_helpers.cpp b/3d-viewer/3d_cache/sg/sg_helpers.cpp index dbc475006d..219801a6a3 100644 --- a/3d-viewer/3d_cache/sg/sg_helpers.cpp +++ b/3d-viewer/3d_cache/sg/sg_helpers.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "3d_cache/sg/sg_helpers.h" #include "3d_cache/sg/sg_node.h" @@ -306,3 +308,192 @@ bool S3D::ReadColor( std::ifstream& aFile, SGCOLOR& aColor ) return true; } + + +struct TRIAD +{ + int p1; + int p2; + int p3; +}; + + +bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords, + std::vector< int >& index, std::vector< SGVECTOR >& norms ) +{ + size_t vsize = coords.size(); + + if( vsize < 3 ) + { + #ifdef DEBUG + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [INFO] invalid vertex set (fewer than 3 vertices)\n"; + #endif + + return false; + } + + size_t isize = index.size(); + + if( 0 != isize % 3 || index.empty() ) + { + #ifdef DEBUG + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [INFO] invalid index set (not multiple of 3)\n"; + #endif + + return false; + } + + if( !norms.empty() ) + { + #ifdef DEBUG + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [INFO] normals set is not empty\n"; + #endif + + return false; + } + + std::map< int, std::list< TRIAD > >vmap; + + int p1, p2, p3; + + // create the map of indices to facet sets + for( size_t i = 0; i < isize; ) + { + p1 = index[i++]; + p2 = index[i++]; + p3 = index[i++]; + + if( p1 < 0 || p1 >= vsize || p2 < 0 || p2 >= vsize || + p3 < 0 || p3 >= vsize ) + { + #ifdef DEBUG + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [INFO] invalid index set; index out of bounds\n"; + #endif + + return false; + } + + // ignore degenerate triangle indices; note that it is still possible to + // have degenerate vertices and these may cause problems + if( p1 == p2 || p2 == p3 || p3 == p1 ) + continue; + + TRIAD tri; + tri.p1 = p1; + tri.p2 = p2; + tri.p3 = p3; + + std::map< int, std::list< TRIAD > >::iterator ip = vmap.find( p1 ); + + if( ip != vmap.end() ) + { + ip->second.push_back( tri ); + } + else + { + vmap.insert( std::pair < int, std::list < TRIAD > > ( p1, std::list < TRIAD >( 1, tri ) ) ); + } + + ip = vmap.find( p2 ); + + if( ip != vmap.end() ) + { + ip->second.push_back( tri ); + } + else + { + vmap.insert( std::pair < int, std::list < TRIAD > > ( p2, std::list < TRIAD >( 1, tri ) ) ); + } + + ip = vmap.find( p2 ); + + if( ip != vmap.end() ) + { + ip->second.push_back( tri ); + } + else + { + vmap.insert( std::pair < int, std::list < TRIAD > > ( p3, std::list < TRIAD >( 1, tri ) ) ); + } + } + + std::map< int, std::list< TRIAD > >::iterator sM = vmap.begin(); + std::map< int, std::list< TRIAD > >::iterator eM = vmap.end(); + size_t idx = 0; + + while( sM != eM ) + { + size_t item = sM->first; + + // assign any skipped coordinates a normal of (0,0,1) + while( item > idx ) + { + norms.push_back( SGVECTOR( 0, 0, 1 ) ); + ++idx; + } + + std::list< TRIAD >::iterator sT = sM->second.begin(); + std::list< TRIAD >::iterator eT = sM->second.end(); + + double nx = 0.0; + double ny = 0.0; + double nz = 0.0; + + // XXX - TODO: + // eliminate equal face normals in order to prevent distortion of the true vertex normal + + while( sT != eT ) + { + double x0, x1, x2; + double y0, y1, y2; + double z0, z1, z2; + + x1 = coords[sT->p2].x - coords[sT->p1].x; + y1 = coords[sT->p2].y - coords[sT->p1].y; + z1 = coords[sT->p2].z - coords[sT->p1].z; + + x2 = coords[sT->p3].x - coords[sT->p1].x; + y2 = coords[sT->p3].y - coords[sT->p1].y; + z2 = coords[sT->p3].z - coords[sT->p1].z; + + x0 = (y1 * z2) - (z1 * y2); + y0 = (z1 * x2) - (x1 * z2); + z0 = (x1 * y2) - (y1 * x2); + + double m = sqrt( x0*x0 + y0*y0 + z0*z0 ); + + // add the normal to the normal accumulated for this facet + + if( m < 1e-12 ) + { + nz += 1.0; + } + else + { + nx += ( x0 / m ); + ny += ( y0 / m ); + nz += ( z0 / m ); + } + + ++sT; + } + + norms.push_back( SGVECTOR( nx, ny, nz ) ); + + ++idx; + ++sM; + } + + if( norms.size() != coords.size() ) + { + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [BUG] number of normals does not equal number of vertices\n"; + return false; + } + + return true; +} diff --git a/3d-viewer/3d_cache/sg/sg_helpers.h b/3d-viewer/3d_cache/sg/sg_helpers.h index 15d55d3283..7cf6ce8f8f 100644 --- a/3d-viewer/3d_cache/sg/sg_helpers.h +++ b/3d-viewer/3d_cache/sg/sg_helpers.h @@ -35,9 +35,14 @@ #include #include #include +#include #include "plugins/3dapi/sg_base.h" #include "plugins/3dapi/sg_types.h" +class SGNORMALS; +class SGCOORDS; +class SGCOORDINDEX; + // Function to drop references within an SGNODE // The node being destroyed must remove itself from the object reference's // backpointer list in order to avoid a segfault. @@ -148,6 +153,29 @@ namespace S3D { + // + // Normals calculations from triangles + // + + /* + * Function CalcTriangleNormals + * takes an array of 3D coordinates and its corresponding index set and calculates + * the normals assuming that indices are given in CCW order. Care must be taken in + * using this function to ensure that: + * (a) all coordinates are indexed; unindexed coordinates are assigned normal(0,0,1); + * when dealing with VRML models which may list and reuse one large coordinate set it + * is necessary to gather all index sets and perform this operation only once. + * (b) index sets must represent triangles (multiple of 3 indices) and must not be + * degenerate - that is all indices and coordinates in a triad must be unique. + * + * @param coords is the array of 3D vertices + * @param index is the array of 3x vertex indices (triads) + * @param norms is an empty array which holds the normals corresponding to each vector + * @return true on success; otherwise false. + */ + bool CalcTriangleNormals( std::vector< SGPOINT > coords, std::vector< int >& index, + std::vector< SGVECTOR >& norms ); + // // VRML related functions // diff --git a/3d-viewer/3d_cache/sg/sg_node.h b/3d-viewer/3d_cache/sg/sg_node.h index 01b3938b88..a337ea5c52 100644 --- a/3d-viewer/3d_cache/sg/sg_node.h +++ b/3d-viewer/3d_cache/sg/sg_node.h @@ -76,10 +76,10 @@ namespace S3D class SGNODE { private: - std::list< SGNODE* > m_BackPointers; // nodes which hold a reference to this SGNODE** m_Association; // handle to the instance held by a wrapper protected: + std::list< SGNODE* > m_BackPointers; // nodes which hold a reference to this SGNODE* m_Parent; // pointer to parent node; may be NULL for top level transform S3D::SGTYPES m_SGtype; // type of SG node std::string m_Name; // name to use for referencing the entity by name diff --git a/include/plugins/3dapi/ifsg_faceset.h b/include/plugins/3dapi/ifsg_faceset.h index 85be5370b3..98a7d6de9e 100644 --- a/include/plugins/3dapi/ifsg_faceset.h +++ b/include/plugins/3dapi/ifsg_faceset.h @@ -47,6 +47,8 @@ public: bool Attach( SGNODE* aNode ); bool NewNode( SGNODE* aParent ); bool NewNode( IFSG_NODE& aParent ); + + bool CalcNormals( void ); }; #endif // IFSG_FACESET_H diff --git a/plugins/3d/idf/s3d_plugin_idf.cpp b/plugins/3d/idf/s3d_plugin_idf.cpp index 969a7580db..7b11495ece 100644 --- a/plugins/3d/idf/s3d_plugin_idf.cpp +++ b/plugins/3d/idf/s3d_plugin_idf.cpp @@ -204,14 +204,13 @@ SCENEGRAPH* Load( char const* aFileName ) IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face ); coordIdx->SetIndices( indices.size(), &indices[0] ); - // XXX - TO BE IMPLEMENTED : add correct normals and colors - std::vector< SGVECTOR > norms; - - for( size_t i = 0; i < nvert; ++i ) - norms.push_back( SGVECTOR( 0.0, 0.0, 1.0 ) ); - - IFSG_NORMALS* np = new IFSG_NORMALS( *face ); - np->SetNormalList( nvert, &norms[0] ); + if( !face->CalcNormals() ) + { + #ifdef DEBUG + std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; + std::cerr << " * [INFO] cannot calculate normals\n"; + #endif + } // magenta IFSG_APPEARANCE* material = new IFSG_APPEARANCE( *shape); @@ -222,13 +221,20 @@ SCENEGRAPH* Load( char const* aFileName ) SCENEGRAPH* data = (SCENEGRAPH*)tx0->GetRawPtr(); + // XXX - DEBUG - WRITE OUT IDF FILE TO CONFIRM NORMALS + wxFileName fn( aFileName ); + wxString fnam = fn.GetName(); + fnam.append( wxT(".wrl") ); + std::cerr << "XXX: FILE NAME: " << fnam.ToUTF8() << "\n"; + S3D::WriteVRML( fnam, true, (SGNODE*)(data), true, true ); + + // delete the API wrappers delete shape; delete face; delete coordIdx; delete material; delete cp; - delete np; delete tx0; return data; diff --git a/plugins/ldr/pluginldr.cpp b/plugins/ldr/pluginldr.cpp index 9b0c7a91eb..2d54ced08c 100644 --- a/plugins/ldr/pluginldr.cpp +++ b/plugins/ldr/pluginldr.cpp @@ -84,7 +84,7 @@ bool KICAD_PLUGIN_LDR::open( const wxString& aFullFileName, const char* aPluginC LINK_ITEM( m_getClassVersion, GET_CLASS_VERSION, "GetClassVersion" ); LINK_ITEM( m_checkClassVersion, CHECK_CLASS_VERSION , "CheckClassVersion" ); LINK_ITEM( m_getPluginName, GET_PLUGIN_NAME, "GetKicadPluginName" ); - LINK_ITEM( m_getVersion, GET_VERSION, "GetVersion" ); + LINK_ITEM( m_getVersion, GET_VERSION, "GetPluginVersion" ); #ifdef DEBUG bool fail = false;