/* vertexData.cpp Copyright (c) 2007, Tobias Wolf All rights reserved. Implementation of the VertexData class. */ /** note, derived from Equalizer LGPL source.*/ #include "typedefs.h" #include "vertexData.h" #include "ply.h" #include #include #include #include #include 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 }