+ Improved the normals calculations (however it's still buggy)

+ Reworked 3D plugins to use normals calculations
This commit is contained in:
Cirilo Bernardo 2015-12-16 14:25:46 +11:00
parent 304cd81ebd
commit c251fbac75
11 changed files with 126 additions and 108 deletions

View File

@ -161,10 +161,10 @@ bool IFSG_FACESET::NewNode( IFSG_NODE& aParent )
}
bool IFSG_FACESET::CalcNormals( void )
bool IFSG_FACESET::CalcNormals( SGNODE** aPtr )
{
if( m_node )
return ((SGFACESET*)m_node)->CalcNormals();
return ((SGFACESET*)m_node)->CalcNormals( aPtr );
return false;
}

View File

@ -289,12 +289,9 @@ void SGVECTOR::normalize( void )
double dx = vx * vx;
double dy = vy * vy;
double dz = vz * vz;
double dv2 = dx + dy + dz;
double dv2 = sqrt( dx + dy + dz );
// note: placing the limit at 1e-14 ensures that
// models representing mm to 1e-4 precision can
// be accepted before the calculations blow up
if( (dx + dy + dz) < 1e-14 )
if( (dx + dy + dz) < 1e-8 )
{
// use the default; the numbers are too small
// to be believable
@ -304,13 +301,9 @@ void SGVECTOR::normalize( void )
return;
}
dx /= dv2;
dy /= dv2;
dz /= dv2;
vx = sqrt( dx );
vy = sqrt( dy );
vz = sqrt( dz );
vx /= dv2;
vy /= dv2;
vz /= dv2;
return;
}

View File

@ -316,8 +316,11 @@ bool SGCOORDS::ReadCache( std::ifstream& aFile, SGNODE* parentNode )
}
bool SGCOORDS::CalcNormals( void )
bool SGCOORDS::CalcNormals( SGNODE** aPtr )
{
if( aPtr )
*aPtr = NULL;
if( NULL == m_Parent )
return false;
@ -342,7 +345,12 @@ bool SGCOORDS::CalcNormals( void )
np = new SGNORMALS( m_Parent );
if( S3D::CalcTriangleNormals( coords, ilist, np->norms ) )
{
if( aPtr )
*aPtr = np;
return true;
}
delete np;

View File

@ -60,7 +60,7 @@ public:
* calculates normals for this coordinate list and sets the
* normals list in the parent SGFACESET
*/
bool CalcNormals( void );
bool CalcNormals( SGNODE** aPtr );
void ReNameNodes( void );
bool WriteVRML( std::ofstream& aFile, bool aReuseFlag );

View File

@ -1041,7 +1041,7 @@ void SGFACESET::GatherCoordIndices( std::vector< int >& aIndexList )
}
bool SGFACESET::CalcNormals( void )
bool SGFACESET::CalcNormals( SGNODE** aPtr )
{
if( m_RCoords )
{
@ -1050,7 +1050,7 @@ bool SGFACESET::CalcNormals( void )
if( !fp )
return false;
return fp->CalcNormals();
return fp->CalcNormals( aPtr );
}
if( NULL == m_Coords || m_Coords->coords.empty() )
@ -1059,5 +1059,5 @@ bool SGFACESET::CalcNormals( void )
if( m_Normals && !m_Normals->norms.empty( ) )
return true;
return m_Coords->CalcNormals();
return m_Coords->CalcNormals( aPtr );
}

View File

@ -76,7 +76,7 @@ public:
bool AddRefNode( SGNODE* aNode );
bool AddChildNode( SGNODE* aNode );
bool CalcNormals( void );
bool CalcNormals( SGNODE** aPtr );
void ReNameNodes( void );
bool WriteVRML( std::ofstream& aFile, bool aReuseFlag );

View File

@ -32,7 +32,8 @@
#include "3d_cache/sg/sg_helpers.h"
#include "3d_cache/sg/sg_node.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
// formats a floating point number for text output to a VRML file
void S3D::FormatFloat( std::string& result, double value )
@ -310,14 +311,50 @@ bool S3D::ReadColor( std::ifstream& aFile, SGCOLOR& aColor )
}
struct TRIAD
static bool degenerate( glm::dvec3* pts )
{
int p1;
int p2;
int p3;
double dx, dy, dz;
dx = pts[1].x - pts[0].x;
dy = pts[1].y - pts[0].y;
dz = pts[1].z - pts[0].z;
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
return true;
dx = pts[2].x - pts[0].x;
dy = pts[2].y - pts[0].y;
dz = pts[2].z - pts[0].z;
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
return true;
dx = pts[2].x - pts[1].x;
dy = pts[2].y - pts[1].y;
dz = pts[2].z - pts[1].z;
if( ( dx*dx + dy*dy + dz*dz ) < 1e-15 )
return true;
return false;
};
static void calcTriad( glm::dvec3* pts, glm::dvec3& tri )
{
if( degenerate( pts ) )
{
tri = glm::dvec3( 0.0, 0.0, 0.0 );
return;
}
// normal * 2 * area
tri = cross( pts[1] - pts[0], pts[2] - pts[0] );
return;
}
bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
std::vector< int >& index, std::vector< SGVECTOR >& norms )
{
@ -355,7 +392,7 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
return false;
}
std::map< int, std::list< TRIAD > >vmap;
std::map< int, std::list< glm::dvec3 > >vmap;
int p1, p2, p3;
@ -377,17 +414,14 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
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;
glm::dvec3 tri;
glm::dvec3 trip[3];
trip[0] = glm::dvec3( coords[p1].x, coords[p1].y, coords[p1].z );
trip[1] = glm::dvec3( coords[p2].x, coords[p2].y, coords[p2].z );
trip[2] = glm::dvec3( coords[p3].x, coords[p3].y, coords[p3].z );
calcTriad( trip, tri );
TRIAD tri;
tri.p1 = p1;
tri.p2 = p2;
tri.p3 = p3;
std::map< int, std::list< TRIAD > >::iterator ip = vmap.find( p1 );
std::map< int, std::list< glm::dvec3 > >::iterator ip = vmap.find( p1 );
if( ip != vmap.end() )
{
@ -395,7 +429,8 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
}
else
{
vmap.insert( std::pair < int, std::list < TRIAD > > ( p1, std::list < TRIAD >( 1, tri ) ) );
vmap.insert( std::pair < int, std::list < glm::dvec3 > >
( p1, std::list < glm::dvec3 >( 1, tri ) ) );
}
ip = vmap.find( p2 );
@ -406,10 +441,11 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
}
else
{
vmap.insert( std::pair < int, std::list < TRIAD > > ( p2, std::list < TRIAD >( 1, tri ) ) );
vmap.insert( std::pair < int, std::list < glm::dvec3 > >
( p2, std::list < glm::dvec3 >( 1, tri ) ) );
}
ip = vmap.find( p2 );
ip = vmap.find( p3 );
if( ip != vmap.end() )
{
@ -417,12 +453,13 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
}
else
{
vmap.insert( std::pair < int, std::list < TRIAD > > ( p3, std::list < TRIAD >( 1, tri ) ) );
vmap.insert( std::pair < int, std::list < glm::dvec3 > >
( p3, std::list < glm::dvec3 >( 1, tri ) ) );
}
}
std::map< int, std::list< TRIAD > >::iterator sM = vmap.begin();
std::map< int, std::list< TRIAD > >::iterator eM = vmap.end();
std::map< int, std::list< glm::dvec3 > >::iterator sM = vmap.begin();
std::map< int, std::list< glm::dvec3 > >::iterator eM = vmap.end();
size_t idx = 0;
while( sM != eM )
@ -436,53 +473,17 @@ bool S3D::CalcTriangleNormals( std::vector< SGPOINT > coords,
++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
std::list< glm::dvec3 >::iterator sT = sM->second.begin();
std::list< glm::dvec3 >::iterator eT = sM->second.end();
glm::dvec3 norm( 0.0, 0.0, 0.0 );
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 );
}
norm += *sT;
++sT;
}
norms.push_back( SGVECTOR( nx, ny, nz ) );
norms.push_back( SGVECTOR( norm.x, norm.y, norm.z ) );
++idx;
++sM;

View File

@ -48,7 +48,7 @@ public:
bool NewNode( SGNODE* aParent );
bool NewNode( IFSG_NODE& aParent );
bool CalcNormals( void );
bool CalcNormals( SGNODE** aPtr );
};
#endif // IFSG_FACESET_H

View File

@ -199,15 +199,13 @@ SCENEGRAPH* Load( char const* aFileName )
int cidx[12] = { 0, 3, 1, 0, 2, 3, 1, 3, 2, 0, 1, 2 };
coordIdx->SetIndices( 3, cidx );
// the vertex normals in this case are the normalized
// vertex points
SGVECTOR norm[4];
norm[0] = SGVECTOR( -1.0, 0.0, -SQ2 );
norm[1] = SGVECTOR( 1.0, 0.0, -SQ2 );
norm[2] = SGVECTOR( 0.0, -1.0, SQ2 );
norm[3] = SGVECTOR( 0.0, 1.0, SQ2 );
IFSG_NORMALS* np = new IFSG_NORMALS( *face );
np->SetNormalList( 4, norm );
// note: track the sets of faces since all faces need to be
// instantiated before we can calculate the normals list;
// this is due to the need for all vertices to be referenced
// in the index lists; however we have a single coordinate
// list and 4 associated vertex lists so the requirement is
// not met until all faces are instantiated.
SGNODE* face1 = face->GetRawPtr();
// create an appearance; appearances are owned by shapes
// magenta
@ -221,9 +219,9 @@ SCENEGRAPH* Load( char const* aFileName )
shape->NewNode( *tx1 );
face->NewNode( *shape );
face->AddRefNode( *cp );
face->AddRefNode( *np );
coordIdx->NewNode( *face );
coordIdx->SetIndices( 3, &cidx[3] );
SGNODE* face2 = face->GetRawPtr();
// red
material->NewNode( *shape );
material->SetSpecular( 1.0, 0.0, 0.0 );
@ -235,9 +233,9 @@ SCENEGRAPH* Load( char const* aFileName )
shape->NewNode( *tx1 );
face->NewNode( *shape );
face->AddRefNode( *cp );
face->AddRefNode( *np );
coordIdx->NewNode( *face );
coordIdx->SetIndices( 3, &cidx[6] );
SGNODE* face3 = face->GetRawPtr();
// green
material->NewNode( *shape );
material->SetSpecular( 0.0, 1.0, 0.0 );
@ -249,9 +247,9 @@ SCENEGRAPH* Load( char const* aFileName )
shape->NewNode( *tx1 );
face->NewNode( *shape );
face->AddRefNode( *cp );
face->AddRefNode( *np );
coordIdx->NewNode( *face );
coordIdx->SetIndices( 3, &cidx[9] );
SGNODE* face4 = face->GetRawPtr();
// blue
material->NewNode( *shape );
material->SetSpecular( 0.0, 0.0, 1.0 );
@ -259,6 +257,21 @@ SCENEGRAPH* Load( char const* aFileName )
material->SetAmbient( 0.9 );
material->SetShininess( 0.3 );
// note: now that the faces are instantiated we
// can calculate the per-vertex normals
SGNODE* np;
face->Attach( face1 );
face->CalcNormals( &np );
if( np )
{
face->Attach( face2 );
face->AddRefNode( np );
face->Attach( face3 );
face->AddRefNode( np );
face->Attach( face4 );
face->AddRefNode( np );
}
// create a copy of the entire tetrahedron shifted Z+2 and rotated 2/3PI
IFSG_TRANSFORM* tx2 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
@ -274,7 +287,6 @@ SCENEGRAPH* Load( char const* aFileName )
delete coordIdx;
delete material;
delete cp;
delete np;
delete tx0;
delete tx1;
delete tx2;

View File

@ -204,7 +204,7 @@ SCENEGRAPH* Load( char const* aFileName )
IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
coordIdx->SetIndices( indices.size(), &indices[0] );
if( !face->CalcNormals() )
if( !face->CalcNormals( NULL ) )
{
#ifdef DEBUG
std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";

View File

@ -1344,28 +1344,32 @@ bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
{
double dx0 = p1->x - p0->x;
double dx1 = p2->x - p0->x;
double dx2 = p2->x - p1->x;
double dy0 = p1->y - p0->y;
double dy1 = p2->y - p0->y;
double dy2 = p2->y - p1->y;
dx0 *= dx0;
dx1 *= dx1;
dx2 *= dx2;
dy0 *= dy0;
dy1 *= dy1;
dy2 *= dy2;
// this number is chosen because we shall only write 9 decimal places
// at most on the VRML output
double err = 0.000000001;
// test if the triangles are degenerate (parallel sides)
if( dx0 < err && dx0 > -err && dx1 < err && dx1 > -err )
// test if the triangles are degenerate (equal points)
if( ( dx0 + dy0 ) < err )
return false;
if( dy0 < err && dy0 > -err && dy1 < err && dy1 > -err )
if( ( dx1 + dy1 ) < err )
return false;
double sl0 = dy0 / dx0;
double sl1 = dy1 / dx1;
double dsl = sl1 - sl0;
if( dsl < err && dsl > -err )
if( ( dx2 + dy2 ) < err )
return false;
triplets.push_back( TRIPLET_3D( p0->o, p1->o, p2->o ) );