Hello Robert, attached is an extension to the PLY plugin to read files with textures.
Regards, Uwe -- \\\|/// *HLRS, High Performance Computing Center Stuttgart* _I_ ( o o ) *Visualization/VR* _I_ (_@_)--oo0O--(_)--O0oo------------------------------------------(_@_) | | Dr.-Ing. Uwe Woessner http://www.hlrs.de/people/woessner/ | | | | .ooo0 mobile: +49-173-7028729 | | |_| ( ) Oooo. office: +49-711-6856-5790 |_| (_@_)-------\ (---( )-----------------------------------------(_@_) I \_) ) / I (_/
/* vertexData.cpp Copyright (c) 2007, Tobias Wolf <tw...@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> #include <osgUtil/SmoothingVisitor> #include <osg/TexEnv> #include <osgDB/ReaderWriter> #include <osgDB/ReadFile> #include <osg/Texture2D> using namespace std; using namespace ply; /* Contructor. */ VertexData::VertexData() : _invertFaces( false ) { // Initialize the members _vertices = NULL; _colors = NULL; _normals = NULL; _triangles = NULL; _diffuse = NULL; _ambient = NULL; _specular = NULL; _texcoord = 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 alpha; 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; float texture_u; float texture_v; } 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 }, { "alpha", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, alpha ), 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 }, { "texture_u", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, texture_u), 0, 0, 0, 0 }, { "texture_v", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, texture_v), 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 & RGBA) ply_get_property( file, "vertex", &vertexProps[9] ); if (fields & AMBIENT) for( int i = 10; i < 13; ++i ) ply_get_property( file, "vertex", &vertexProps[i] ); if (fields & DIFFUSE) for( int i = 13; i < 16; ++i ) ply_get_property( file, "vertex", &vertexProps[i] ); if (fields & SPECULAR) for( int i = 16; i < 21; ++i ) ply_get_property( file, "vertex", &vertexProps[i] ); if (fields & TEXCOORD) for (int i = 21; i < 23; ++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 || fields & RGBA) { 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; } if (fields & TEXCOORD) { if (!_texcoord.valid()) _texcoord = new osg::Vec2Array; } // 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 & RGBA ) _colors->push_back( osg::Vec4( (unsigned int) vertex.red / 255.0, (unsigned int) vertex.green / 255.0 , (unsigned int) vertex.blue / 255.0, (unsigned int) vertex.alpha / 255.0) ); else if( fields & RGB ) _colors->push_back( osg::Vec4( (unsigned int) vertex.red / 255.0, (unsigned int) vertex.green / 255.0 , (unsigned int) vertex.blue / 255.0, 1.0 ) ); if( fields & AMBIENT ) _ambient->push_back( osg::Vec4( (unsigned int) vertex.ambient_red / 255.0, (unsigned int) vertex.ambient_green / 255.0 , (unsigned int) vertex.ambient_blue / 255.0, 1.0 ) ); if( fields & DIFFUSE ) _diffuse->push_back( osg::Vec4( (unsigned int) vertex.diffuse_red / 255.0, (unsigned int) vertex.diffuse_green / 255.0 , (unsigned int) vertex.diffuse_blue / 255.0, 1.0 ) ); if( fields & SPECULAR ) _specular->push_back( osg::Vec4( (unsigned int) vertex.specular_red / 255.0, (unsigned int) vertex.specular_green / 255.0 , (unsigned int) vertex.specular_blue / 255.0, 1.0 ) ); if (fields & TEXCOORD) _texcoord->push_back(osg::Vec2(vertex.texture_u,vertex.texture_v)); } } /* 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|vertex_index", PLY_INT, PLY_INT, offsetof( _Face, vertices ), 1, PLY_UCHAR, PLY_UCHAR, offsetof( _Face, nVertices ) } }; ply_get_property( file, "face", &faceProps[0] ); if(!_triangles.valid()) _triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); if(!_quads.valid()) _quads = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS); const char NUM_VERTICES_TRIANGLE(3); const char NUM_VERTICES_QUAD(4); // read the faces, reversing the reading direction if _invertFaces is true for( int i = 0 ; i < nFaces; i++ ) { // initialize face values face.nVertices = 0; face.vertices = 0; ply_get_element( file, static_cast< void* >( &face ) ); if (face.vertices) { if (face.nVertices == NUM_VERTICES_TRIANGLE || face.nVertices == NUM_VERTICES_QUAD) { unsigned short index; for(int j = 0 ; j < face.nVertices ; j++) { index = ( _invertFaces ? face.nVertices - 1 - j : j ); if(face.nVertices == 4) _quads->push_back(face.vertices[index]); else _triangles->push_back(face.vertices[index]); } } // 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 occurred: " << 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 char *textureFile = NULL; for( int i = 0; i < nComments; i++ ) { if( equal_strings( comments[i], "modified by flipply" ) ) { _invertFaces = true; } if (strncmp(comments[i], "TextureFile",11)==0) { textureFile = comments[i]+12; char * path = new char[strlen(const_cast<char*>(filename)) + 1 + strlen(comments[i])]; if (textureFile[0] == '\\' || textureFile[0] == '/' || textureFile[1] == ':') { // texture filename is absolute strcpy(path, textureFile); } else { // texture filename is relative // add directory of ply file strcpy(path, const_cast<char*>(filename)); char *pp = path + strlen(path); while (pp >= path) { if (*pp == '\\' || *pp == '/') { pp++; *pp = '\0'; break; } pp--; } if (pp == path - 1) { pp++; *pp = '\0'; } strcat(path, textureFile); } textureFile = path; } } 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 occurred: " << 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, "alpha" ) ) fields |= RGBA; 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 (equal_strings(props[j]->name, "texture_u")) fields |= TEXCOORD; if (equal_strings(props[j]->name, "texture_v")) fields |= TEXCOORD; } 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 || fields & RGBA) { 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)); } if (fields & TEXCOORD) { MESHASSERT(_texcoord->size() == static_cast< size_t >(nElems)); } result = true; } catch( exception& e ) { MESHERROR << "Unable to read vertex in PLY file, an exception occurred: " << 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 #if DEBUG unsigned int nbTriangles = (_triangles.valid() ? _triangles->size() / 3 : 0) ; unsigned int nbQuads = (_quads.valid() ? _quads->size() / 4 : 0 ); MESHASSERT( (nbTriangles + nbQuads) == static_cast< size_t >( nElems ) ); #endif result = true; } catch( exception& e ) { MESHERROR << "Unable to read PLY file, an exception occurred: " << 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()); // Add the primitive set bool hasTriOrQuads = false; if (_triangles.valid() && _triangles->size() > 0 ) { geom->addPrimitiveSet(_triangles.get()); hasTriOrQuads = true; } if (_quads.valid() && _quads->size() > 0 ) { geom->addPrimitiveSet(_quads.get()); hasTriOrQuads = true; } // Print points if the file contains unsupported primitives if(!hasTriOrQuads) 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(), osg::Array::BIND_PER_VERTEX ); } else if(_ambient.valid()) { geom->setColorArray(_ambient.get(), osg::Array::BIND_PER_VERTEX ); } else if(_diffuse.valid()) { geom->setColorArray(_diffuse.get(), osg::Array::BIND_PER_VERTEX ); } else if(_specular.valid()) { geom->setColorArray(_specular.get(), osg::Array::BIND_PER_VERTEX ); } else if (_texcoord.valid()) { geom->setTexCoordArray(0, _texcoord); } // If the model has normals, add them to the geometry if(_normals.valid()) { geom->setNormalArray(_normals.get(), osg::Array::BIND_PER_VERTEX); } else { // If not, use the smoothing visitor to generate them // (quads will be triangulated by the smoothing visitor) osgUtil::SmoothingVisitor::smooth((*geom), osg::PI/2); } // set flage true to activate the vertex buffer object of drawable geom->setUseVertexBufferObjects(true); osg::Image *image = NULL; if (textureFile && (image = osgDB::readImageFile(textureFile)) != NULL) { osg::Texture2D *texture = new osg::Texture2D; texture->setImage(image); texture->setResizeNonPowerOfTwoHint(false); osg::TexEnv *texenv = new osg::TexEnv; texenv->setMode(osg::TexEnv::REPLACE); osg::StateSet *stateset = geom->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); stateset->setTextureAttribute(0, texenv); delete[] textureFile; } osg::Geode* geode = new osg::Geode; geode->addDrawable(geom); return geode; } return NULL; }
/* vertexData.h Copyright (c) 2007, Tobias Wolf <tw...@access.unizh.ch> All rights reserved. Header file of the VertexData class. */ /** note, derived from Equalizer LGPL source.*/ #ifndef MESH_VERTEXDATA_H #define MESH_VERTEXDATA_H #include <osg/Node> #include <osg/PrimitiveSet> #include <vector> /////////////////////////////////////////////////////////////////////////////// //! //! \class VertexData //! \brief helps to read ply file and converts in to osg::Node format //! /////////////////////////////////////////////////////////////////////////////// // defined elsewhere struct PlyFile; namespace ply { /* Holds the flat data and offers routines to read, scale and sort it. */ class VertexData { public: // Default constructor VertexData(); // Reads ply file and convert in to osg::Node and returns the same osg::Node* readPlyFile( const char* file, const bool ignoreColors = false ); // to set the flag for using inverted face void useInvertedFaces() { _invertFaces = true; } private: enum VertexFields { NONE = 0, XYZ = 1, NORMALS = 2, RGB = 4, AMBIENT = 8, DIFFUSE = 16, SPECULAR = 32, RGBA = 64, TEXCOORD = 128 }; // Function which reads all the vertices and colors if color info is // given and also if the user wants that information void readVertices( PlyFile* file, const int nVertices, const int vertexFields ); // Reads the triangle indices from the ply file void readTriangles( PlyFile* file, const int nFaces ); bool _invertFaces; // Vertex array in osg format osg::ref_ptr<osg::Vec3Array> _vertices; // Color array in osg format osg::ref_ptr<osg::Vec4Array> _colors; osg::ref_ptr<osg::Vec4Array> _ambient; osg::ref_ptr<osg::Vec4Array> _diffuse; osg::ref_ptr<osg::Vec4Array> _specular; osg::ref_ptr<osg::Vec2Array> _texcoord; // Normals in osg format osg::ref_ptr<osg::Vec3Array> _normals; // The indices of the faces in premitive set osg::ref_ptr<osg::DrawElementsUInt> _triangles; osg::ref_ptr<osg::DrawElementsUInt> _quads; }; } #endif // MESH_VERTEXDATA_H
_______________________________________________ osg-submissions mailing list osg-submissions@lists.openscenegraph.org http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org