Hi,

The plugin for PLY files does not support reading texture coordinates.

The attached files (compatible with Trunk and 3.4 branch, in
src/osgPlugins/ply ) add this capability to the reader of PLY files.

As PLY format is quite flexible, it does not impose the naming of such
properties. The "u" and "v" names have been used in these files (
http://www.okino.com/conv/exp_ply.htm ).

As a sample, box.ply is just a box generated by OpenMesh (
http://www.openmesh.org/). You may notice it has u and v properties. After
applying theses changes, running "osgconv box.ply box.obj" will produce a
valid obj file containing the texture coordinates.

Bye,

-- 
Aitor Moreno
   aitormoreno [@] gmail.com
/*
    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>

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;
	_textcoords = 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           u;
		float           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 },
		{ "u", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, u), 0, 0, 0, 0 },
		{ "v", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, 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 & TEXTCOORDS)
		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 & TEXTCOORDS)
	{
		if (!_textcoords.valid())
			_textcoords = 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 & TEXTCOORDS)
			_textcoords->push_back( osg::Vec2( vertex.u, vertex.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] );

    //triangles.clear();
    //triangles.reserve( nFaces );
    if(!_triangles.valid())
        _triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
    if(!_quads.valid())
        _quads = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);


    // read the faces, reversing the reading direction if _invertFaces is true
    for( int i = 0 ; i < nFaces; i++ )
    {
        ply_get_element( file, static_cast< void* >( &face ) );
        MESHASSERT( face.vertices != 0 );
        if( (unsigned int)(face.nVertices) > 4 )
        {
            free( face.vertices );
            throw MeshException( "Error reading PLY file. Encountered a "
                                 "face which does not have three or four vertices." );
        }

        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

    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 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, "u"))
					fields |= TEXTCOORDS;
          }

            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 & TEXTCOORDS)
				{
					MESHASSERT(_textcoords->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 );
        }

        // 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);
        }

		if (_textcoords.valid()){
			geom->setTexCoordArray(0, _textcoords);
		}

        // 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;
}


/*
    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,
		  TEXTCOORDS = 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;
		
		// texture coordinates array in osg format
		osg::ref_ptr<osg::Vec2Array>   _textcoords;

        // 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

Attachment: box.ply
Description: Binary data

_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to