407 lines
12 KiB
C++
407 lines
12 KiB
C++
/*
|
|
vertexData.cpp
|
|
Copyright (c) 2007, Tobias Wolf <twolf@access.unizh.ch>
|
|
All rights reserved.
|
|
|
|
Implementation of the VertexData class.
|
|
*/
|
|
|
|
/** note, derived from Equalizer LGPL source.*/
|
|
|
|
#include "typedefs.h"
|
|
#include "vertexData.h"
|
|
#include "ply.h"
|
|
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
#include <osg/Geometry>
|
|
#include <osg/Geode>
|
|
#include <osg/io_utils>
|
|
|
|
using namespace std;
|
|
using namespace ply;
|
|
|
|
|
|
struct Normal{
|
|
osg::Vec3 triNormal;
|
|
void normal(osg::Vec3 v1, osg::Vec3 v2, osg::Vec3 v3)
|
|
{
|
|
osg::Vec3 u,v;
|
|
|
|
// right hand system, CCW triangle
|
|
u = v2 - v1;
|
|
v = v3 - v1;
|
|
triNormal = u^v;
|
|
triNormal.normalize();
|
|
}
|
|
};
|
|
|
|
/* Contructor. */
|
|
VertexData::VertexData()
|
|
: _invertFaces( false )
|
|
{
|
|
// Initialize the members
|
|
_vertices = NULL;
|
|
_colors = NULL;
|
|
_normals = NULL;
|
|
_triangles = NULL;
|
|
|
|
}
|
|
|
|
|
|
/* Read the vertex and (if available/wanted) color data from the open file. */
|
|
void VertexData::readVertices( PlyFile* file, const int nVertices,
|
|
const bool readColors )
|
|
{
|
|
// temporary vertex structure for ply loading
|
|
struct _Vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
unsigned char r;
|
|
unsigned char g;
|
|
unsigned char b;
|
|
} vertex;
|
|
|
|
PlyProperty vertexProps[] =
|
|
{
|
|
{ "x", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, x ), 0, 0, 0, 0 },
|
|
{ "y", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, y ), 0, 0, 0, 0 },
|
|
{ "z", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, z ), 0, 0, 0, 0 },
|
|
{ "red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, r ), 0, 0, 0, 0 },
|
|
{ "green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, g ), 0, 0, 0, 0 },
|
|
{ "blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, b ), 0, 0, 0, 0 }
|
|
};
|
|
|
|
// use all 6 properties when reading colors, only the first 3 otherwise
|
|
int limit = readColors ? 6 : 3;
|
|
for( int i = 0; i < limit; ++i )
|
|
ply_get_property( file, "vertex", &vertexProps[i] );
|
|
|
|
// check whether array is valid otherwise allocate the space
|
|
if(!_vertices.valid())
|
|
_vertices = new osg::Vec3Array;
|
|
|
|
// If read colors allocate space for color array
|
|
if( readColors )
|
|
{
|
|
if(!_colors.valid())
|
|
_colors = new osg::Vec4Array;
|
|
}
|
|
|
|
// read in the vertices
|
|
for( int i = 0; i < nVertices; ++i )
|
|
{
|
|
ply_get_element( file, static_cast< void* >( &vertex ) );
|
|
_vertices->push_back( osg::Vec3( vertex.x, vertex.y, vertex.z ) );
|
|
if( readColors )
|
|
_colors->push_back( osg::Vec4( (unsigned int) vertex.r / 256.0, (unsigned int) vertex.g / 256.0 , (unsigned int) vertex.b/ 256.0, 0.0 ) );
|
|
}
|
|
}
|
|
|
|
|
|
/* Read the index data from the open file. */
|
|
void VertexData::readTriangles( PlyFile* file, const int nFaces )
|
|
{
|
|
// temporary face structure for ply loading
|
|
struct _Face
|
|
{
|
|
unsigned char nVertices;
|
|
int* vertices;
|
|
} face;
|
|
|
|
PlyProperty faceProps[] =
|
|
{
|
|
{ "vertex_indices", PLY_INT, PLY_INT, offsetof( _Face, vertices ),
|
|
1, PLY_UCHAR, PLY_UCHAR, offsetof( _Face, nVertices ) }
|
|
};
|
|
|
|
ply_get_property( file, "face", &faceProps[0] );
|
|
|
|
//triangles.clear();
|
|
//triangles.reserve( nFaces );
|
|
if(!_triangles.valid())
|
|
_triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
|
|
|
|
|
|
// read in the faces, asserting that they are only triangles
|
|
uint8_t ind1 = _invertFaces ? 2 : 0;
|
|
uint8_t ind3 = _invertFaces ? 0 : 2;
|
|
for( int i = 0; i < nFaces; ++i )
|
|
{
|
|
ply_get_element( file, static_cast< void* >( &face ) );
|
|
MESHASSERT( face.vertices != 0 );
|
|
if( (unsigned int)(face.nVertices) != 3 )
|
|
{
|
|
free( face.vertices );
|
|
throw MeshException( "Error reading PLY file. Encountered a "
|
|
"face which does not have three vertices." );
|
|
}
|
|
// Add the face indices in the premitive set
|
|
_triangles->push_back( face.vertices[ind1]);
|
|
_triangles->push_back( face.vertices[1]);
|
|
_triangles->push_back( face.vertices[ind3] );
|
|
|
|
// free the memory that was allocated by ply_get_element
|
|
free( face.vertices );
|
|
}
|
|
}
|
|
|
|
|
|
/* Open a PLY file and read vertex, color and index data. and returns the node */
|
|
osg::Node* VertexData::readPlyFile( const char* filename, const bool ignoreColors )
|
|
{
|
|
int nPlyElems;
|
|
char** elemNames;
|
|
int fileType;
|
|
float version;
|
|
bool result = false;
|
|
int nComments;
|
|
char** comments;
|
|
|
|
PlyFile* file = NULL;
|
|
|
|
// Try to open ply file as for reading
|
|
try{
|
|
file = ply_open_for_reading( const_cast< char* >( filename ),
|
|
&nPlyElems, &elemNames,
|
|
&fileType, &version );
|
|
}
|
|
// Catch the if any exception thrown
|
|
catch( exception& e )
|
|
{
|
|
MESHERROR << "Unable to read PLY file, an exception occured: "
|
|
<< e.what() << endl;
|
|
}
|
|
|
|
if( !file )
|
|
{
|
|
MESHERROR << "Unable to open PLY file " << filename
|
|
<< " for reading." << endl;
|
|
return NULL;
|
|
}
|
|
|
|
MESHASSERT( elemNames != 0 );
|
|
|
|
|
|
nComments = file->num_comments;
|
|
comments = file->comments;
|
|
|
|
|
|
#ifndef NDEBUG
|
|
MESHINFO << filename << ": " << nPlyElems << " elements, file type = "
|
|
<< fileType << ", version = " << version << endl;
|
|
#endif
|
|
|
|
for( int i = 0; i < nComments; i++ )
|
|
{
|
|
if( equal_strings( comments[i], "modified by flipply" ) )
|
|
{
|
|
_invertFaces = true;
|
|
}
|
|
|
|
}
|
|
for( int i = 0; i < nPlyElems; ++i )
|
|
{
|
|
int nElems;
|
|
int nProps;
|
|
|
|
PlyProperty** props = NULL;
|
|
try{
|
|
props = ply_get_element_description( file, elemNames[i],
|
|
&nElems, &nProps );
|
|
}
|
|
catch( exception& e )
|
|
{
|
|
MESHERROR << "Unable to get PLY file description, an exception occured: "
|
|
<< e.what() << endl;
|
|
}
|
|
MESHASSERT( props != 0 );
|
|
|
|
#ifndef NDEBUG
|
|
MESHINFO << "element " << i << ": name = " << elemNames[i] << ", "
|
|
<< nProps << " properties, " << nElems << " elements" << endl;
|
|
for( int j = 0; j < nProps; ++j )
|
|
{
|
|
MESHINFO << "element " << i << ", property " << j << ": "
|
|
<< "name = " << props[j]->name << endl;
|
|
}
|
|
#endif
|
|
|
|
// if the string is vertex means vertex data is started
|
|
if( equal_strings( elemNames[i], "vertex" ) )
|
|
{
|
|
bool hasColors = false;
|
|
// determine if the file stores vertex colors
|
|
for( int j = 0; j < nProps; ++j )
|
|
// if the string have the red means color info is there
|
|
if( equal_strings( props[j]->name, "red" ) )
|
|
hasColors = true;
|
|
|
|
if( ignoreColors )
|
|
MESHINFO << "Colors in PLY file ignored per request." << endl;
|
|
|
|
try {
|
|
// Read vertices and store in a std::vector array
|
|
readVertices( file, nElems, hasColors && !ignoreColors );
|
|
// Check whether all vertices are loaded or not
|
|
MESHASSERT( _vertices->size() == static_cast< size_t >( nElems ) );
|
|
// Check all color elements read or not
|
|
if( hasColors && !ignoreColors )
|
|
{
|
|
MESHASSERT( _colors->size() == static_cast< size_t >( nElems ) );
|
|
}
|
|
}
|
|
catch( exception& e )
|
|
{
|
|
MESHERROR << "Unable to read vertex in PLY file, an exception occured: "
|
|
<< e.what() << endl;
|
|
// stop for loop by setting the loop variable to break condition
|
|
// this way resources still get released even on error cases
|
|
i = nPlyElems;
|
|
|
|
}
|
|
}
|
|
// If the string is face means triangle info started
|
|
else if( equal_strings( elemNames[i], "face" ) )
|
|
try
|
|
{
|
|
// Read Triangles
|
|
readTriangles( file, nElems );
|
|
// Check whether all face elements read or not
|
|
MESHASSERT( _triangles->size()/3 == static_cast< size_t >( nElems ) );
|
|
result = true;
|
|
}
|
|
catch( exception& e )
|
|
{
|
|
MESHERROR << "Unable to read PLY file, an exception occured: "
|
|
<< e.what() << endl;
|
|
// stop for loop by setting the loop variable to break condition
|
|
// this way resources still get released even on error cases
|
|
i = nPlyElems;
|
|
}
|
|
|
|
// free the memory that was allocated by ply_get_element_description
|
|
for( int j = 0; j < nProps; ++j )
|
|
free( props[j] );
|
|
free( props );
|
|
}
|
|
|
|
ply_close( file );
|
|
|
|
// free the memory that was allocated by ply_open_for_reading
|
|
for( int i = 0; i < nPlyElems; ++i )
|
|
free( elemNames[i] );
|
|
free( elemNames );
|
|
|
|
// If the result is true means the ply file is successfully read
|
|
if(result)
|
|
{
|
|
// Create geometry node
|
|
osg::Geometry* geom = new osg::Geometry;
|
|
|
|
// set the vertex array
|
|
geom->setVertexArray(_vertices.get());
|
|
|
|
// If the normals are not calculated calculate the normals for faces
|
|
if(!_normals.valid())
|
|
_calculateNormals();
|
|
|
|
|
|
// set the normals
|
|
geom->setNormalArray(_normals.get());
|
|
geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
|
|
|
|
// Add the premetive set
|
|
geom->addPrimitiveSet(_triangles.get());
|
|
|
|
// if color info is given set the color array
|
|
if(_colors.valid())
|
|
{
|
|
geom->setColorArray(_colors.get());
|
|
geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
|
|
|
|
}
|
|
|
|
|
|
// set flage true to activate the vertex buffer object of drawable
|
|
geom->setUseVertexBufferObjects(true);
|
|
|
|
|
|
osg::Geode* geode = new osg::Geode;
|
|
geode->addDrawable(geom);
|
|
return geode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Calculate the face or vertex normals of the current vertex data. */
|
|
void VertexData::_calculateNormals( const bool vertexNormals )
|
|
{
|
|
|
|
if(_normals.valid())
|
|
return;
|
|
|
|
#ifndef NDEBUG
|
|
int wrongNormals = 0;
|
|
#endif
|
|
|
|
if(!_normals.valid())
|
|
{
|
|
_normals = new osg::Vec3Array;
|
|
}
|
|
|
|
//normals.clear();
|
|
if( vertexNormals )
|
|
{
|
|
// initialize all normals to zero
|
|
for( size_t i = 0; i < _vertices->size(); ++i )
|
|
{
|
|
_normals->push_back( osg::Vec3( 0, 0, 0 ) );
|
|
}
|
|
}
|
|
|
|
|
|
for( size_t i = 0; i < ((_triangles->size())); i += 3 )
|
|
{
|
|
// iterate over all triangles and add their normals to adjacent vertices
|
|
Normal triangleNormal;
|
|
unsigned int i0, i1, i2;
|
|
i0 = (*_triangles)[i+0];
|
|
i1 = (*_triangles)[i+1];
|
|
i2 = (*_triangles)[i+2];
|
|
triangleNormal.normal((*_vertices)[i0],
|
|
(*_vertices)[i1],
|
|
(*_vertices)[i2] );
|
|
|
|
// count emtpy normals in debug mode
|
|
#ifndef NDEBUG
|
|
if( triangleNormal.triNormal.length() == 0.0f )
|
|
++wrongNormals;
|
|
#endif
|
|
|
|
if( vertexNormals )
|
|
{
|
|
(*_normals)[i0] += triangleNormal.triNormal;
|
|
(*_normals)[i1] += triangleNormal.triNormal;
|
|
(*_normals)[i2] += triangleNormal.triNormal;
|
|
}
|
|
else
|
|
_normals->push_back( triangleNormal.triNormal );
|
|
}
|
|
|
|
// normalize all the normals
|
|
if( vertexNormals )
|
|
for( size_t i = 0; i < _normals->size(); ++i )
|
|
(*_normals)[i].normalize();
|
|
|
|
#ifndef NDEBUG
|
|
if( wrongNormals > 0 )
|
|
MESHINFO << wrongNormals << " faces had no valid normal." << endl;
|
|
#endif
|
|
}
|
|
|