Files
OpenSceneGraph/src/osgPlugins/ply/vertexData.cpp
2012-03-21 17:36:20 +00:00

557 lines
18 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;
_diffuse = NULL;
_ambient = NULL;
_specular = NULL;
}
/* Read the vertex and (if available/wanted) color data from the open file. */
void VertexData::readVertices( PlyFile* file, const int nVertices,
const int fields )
{
// temporary vertex structure for ply loading
struct _Vertex
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned char ambient_red;
unsigned char ambient_green;
unsigned char ambient_blue;
unsigned char diffuse_red;
unsigned char diffuse_green;
unsigned char diffuse_blue;
unsigned char specular_red;
unsigned char specular_green;
unsigned char specular_blue;
float specular_coeff;
float specular_power;
} 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 },
{ "nx", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, nx ), 0, 0, 0, 0 },
{ "ny", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, ny ), 0, 0, 0, 0 },
{ "nz", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, nz ), 0, 0, 0, 0 },
{ "red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, red ), 0, 0, 0, 0 },
{ "green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, green ), 0, 0, 0, 0 },
{ "blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, blue ), 0, 0, 0, 0 },
{ "ambient_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_red ), 0, 0, 0, 0 },
{ "ambient_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_green ), 0, 0, 0, 0 },
{ "ambient_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_blue ), 0, 0, 0, 0 },
{ "diffuse_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_red ), 0, 0, 0, 0 },
{ "diffuse_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_green ), 0, 0, 0, 0 },
{ "diffuse_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_blue ), 0, 0, 0, 0 },
{ "specular_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_red ), 0, 0, 0, 0 },
{ "specular_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_green ), 0, 0, 0, 0 },
{ "specular_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_blue ), 0, 0, 0, 0 },
{ "specular_coeff", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, specular_coeff ), 0, 0, 0, 0 },
{ "specular_power", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, specular_power ), 0, 0, 0, 0 },
};
// use all 6 properties when reading colors, only the first 3 otherwise
for( int i = 0; i < 3; ++i )
ply_get_property( file, "vertex", &vertexProps[i] );
if (fields & NORMALS)
for( int i = 3; i < 6; ++i )
ply_get_property( file, "vertex", &vertexProps[i] );
if (fields & RGB)
for( int i = 6; i < 9; ++i )
ply_get_property( file, "vertex", &vertexProps[i] );
if (fields & AMBIENT)
for( int i = 9; i < 12; ++i )
ply_get_property( file, "vertex", &vertexProps[i] );
if (fields & DIFFUSE)
for( int i = 12; i < 15; ++i )
ply_get_property( file, "vertex", &vertexProps[i] );
if (fields & SPECULAR)
for( int i = 15; i < 20; ++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( fields & NORMALS )
{
if(!_normals.valid())
_normals = new osg::Vec3Array;
}
// If read colors allocate space for color array
if( fields & RGB )
{
if(!_colors.valid())
_colors = new osg::Vec4Array;
}
if( fields & AMBIENT )
{
if(!_ambient.valid())
_ambient = new osg::Vec4Array;
}
if( fields & DIFFUSE )
{
if(!_diffuse.valid())
_diffuse = new osg::Vec4Array;
}
if( fields & SPECULAR )
{
if(!_specular.valid())
_specular = 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 (fields & NORMALS)
_normals->push_back( osg::Vec3( vertex.nx, vertex.ny, vertex.nz ) );
if( fields & RGB )
_colors->push_back( osg::Vec4( (unsigned int) vertex.red / 256.0,
(unsigned int) vertex.green / 256.0 ,
(unsigned int) vertex.blue / 256.0, 0.0 ) );
if( fields & AMBIENT )
_ambient->push_back( osg::Vec4( (unsigned int) vertex.ambient_red / 256.0,
(unsigned int) vertex.ambient_green / 256.0 ,
(unsigned int) vertex.ambient_blue / 256.0, 0.0 ) );
if( fields & DIFFUSE )
_diffuse->push_back( osg::Vec4( (unsigned int) vertex.diffuse_red / 256.0,
(unsigned int) vertex.diffuse_green / 256.0 ,
(unsigned int) vertex.diffuse_blue / 256.0, 0.0 ) );
if( fields & SPECULAR )
_specular->push_back( osg::Vec4( (unsigned int) vertex.specular_red / 256.0,
(unsigned int) vertex.specular_green / 256.0 ,
(unsigned int) vertex.specular_blue / 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
int ind1 = _invertFaces ? 2 : 0;
int 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" ) )
{
int fields = NONE;
// 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, "x" ) )
fields |= XYZ;
if( equal_strings( props[j]->name, "nx" ) )
fields |= NORMALS;
if( equal_strings( props[j]->name, "red" ) )
fields |= RGB;
if( equal_strings( props[j]->name, "ambient" ) )
fields |= AMBIENT;
if( equal_strings( props[j]->name, "diffuse_red" ) )
fields |= DIFFUSE;
if( equal_strings( props[j]->name, "specular_red" ) )
fields |= SPECULAR;
}
if( ignoreColors )
{
fields &= ~(XYZ | NORMALS);
MESHINFO << "Colors in PLY file ignored per request." << endl;
}
try {
// Read vertices and store in a std::vector array
readVertices( file, nElems, fields );
// Check whether all vertices are loaded or not
MESHASSERT( _vertices->size() == static_cast< size_t >( nElems ) );
// Check if all the optional elements were read or not
if( fields & NORMALS )
{
MESHASSERT( _normals->size() == static_cast< size_t >( nElems ) );
}
if( fields & RGB )
{
MESHASSERT( _colors->size() == static_cast< size_t >( nElems ) );
}
if( fields & AMBIENT )
{
MESHASSERT( _ambient->size() == static_cast< size_t >( nElems ) );
}
if( fields & DIFFUSE )
{
MESHASSERT( _diffuse->size() == static_cast< size_t >( nElems ) );
}
if( fields & SPECULAR )
{
MESHASSERT( _specular->size() == static_cast< size_t >( nElems ) );
}
result = true;
}
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(_triangles.valid())
{
if(!_normals.valid())
_calculateNormals();
}
// Set the normals
if (_normals.valid())
{
geom->setNormalArray(_normals.get());
geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
}
// Add the primitive set
if (_triangles.valid() && _triangles->size() > 0 )
geom->addPrimitiveSet(_triangles.get());
else
geom->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, _vertices->size()));
// Apply the colours to the model; at the moment this is a
// kludge because we only use one kind and apply them all the
// same way. Also, the priority order is completely arbitrary
if(_colors.valid())
{
geom->setColorArray(_colors.get());
geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
}
else if(_ambient.valid())
{
geom->setColorArray(_ambient.get());
geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
}
else if(_diffuse.valid())
{
geom->setColorArray(_diffuse.get());
geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
}
else if(_specular.valid())
{
geom->setColorArray(_specular.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
}