+ Bugfix: changed Plugin Loader to expect 'GetPluginVersion' instead of

'GetVersion'.
+ Introduced normal calculations. This is buggy due to multiple equal
  face normals distorting the vertex normal; this will be fixed by
  removing the redundant face normals from the calculations.
This commit is contained in:
Cirilo Bernardo 2015-12-15 18:53:03 +11:00
parent 1940bd71e4
commit 304cd81ebd
13 changed files with 348 additions and 11 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -24,6 +24,8 @@
#include <iostream>
#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;
}

View File

@ -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 );

View File

@ -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();
}

View File

@ -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 );
};
/*

View File

@ -27,6 +27,8 @@
#include <streambuf>
#include <iomanip>
#include <string>
#include <utility>
#include <map>
#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;
}

View File

@ -35,9 +35,14 @@
#include <fstream>
#include <string>
#include <algorithm>
#include <vector>
#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
//

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;