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 <[email protected]>
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 <[email protected]>
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
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org