1 /*
2     vertexData.cpp
3     Copyright (c) 2007, Tobias Wolf <twolf@access.unizh.ch>
4     All rights reserved.
5 
6     Implementation of the VertexData class.
7 */
8 
9 /** note, derived from Equalizer LGPL source.*/
10 
11 #include "typedefs.h"
12 #include "vertexData.h"
13 #include "ply.h"
14 
15 #include <cstdlib>
16 #include <algorithm>
17 #include <osg/Geometry>
18 #include <osg/Geode>
19 #include <osg/io_utils>
20 #include <osgUtil/SmoothingVisitor>
21 #include <osg/TexEnv>
22 #include <osgDB/ReaderWriter>
23 #include <osgDB/FileNameUtils>
24 #include <osgDB/ReadFile>
25 #include <osg/Texture2D>
26 
27 using namespace std;
28 using namespace ply;
29 
30 
31 /*  Constructor.  */
VertexData()32 VertexData::VertexData()
33     : _invertFaces( false )
34 {
35     // Initialize the members
36     _vertices = NULL;
37     _colors = NULL;
38     _normals = NULL;
39     _triangles = NULL;
40     _diffuse = NULL;
41     _ambient = NULL;
42     _specular = NULL;
43     _texcoord = NULL;
44 }
45 
46 
47 /*  Read the vertex and (if available/wanted) color data from the open file.  */
readVertices(PlyFile * file,const int nVertices,const int fields)48 void VertexData::readVertices( PlyFile* file, const int nVertices,
49                                const int fields )
50 {
51     // temporary vertex structure for ply loading
52     struct _Vertex
53     {
54         float           x;
55         float           y;
56         float           z;
57         float           nx;
58         float           ny;
59         float           nz;
60         unsigned char   red;
61         unsigned char   green;
62         unsigned char   blue;
63         unsigned char   alpha;
64         unsigned char   ambient_red;
65         unsigned char   ambient_green;
66         unsigned char   ambient_blue;
67         unsigned char   diffuse_red;
68         unsigned char   diffuse_green;
69         unsigned char   diffuse_blue;
70         unsigned char   specular_red;
71         unsigned char   specular_green;
72         unsigned char   specular_blue;
73         float           specular_coeff;
74         float           specular_power;
75         float texture_u;
76         float texture_v;
77     } vertex;
78 
79     PlyProperty vertexProps[] =
80     {
81         { "x", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, x ), 0, 0, 0, 0 },
82         { "y", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, y ), 0, 0, 0, 0 },
83         { "z", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, z ), 0, 0, 0, 0 },
84         { "nx", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, nx ), 0, 0, 0, 0 },
85         { "ny", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, ny), 0, 0, 0, 0 },
86         { "nz", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, nz), 0, 0, 0, 0 },
87         { "red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, red ), 0, 0, 0, 0 },
88         { "green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, green ), 0, 0, 0, 0 },
89         { "blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, blue ), 0, 0, 0, 0 },
90         { "alpha", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, alpha ), 0, 0, 0, 0 },
91         { "ambient_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_red ), 0, 0, 0, 0 },
92         { "ambient_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_green ), 0, 0, 0, 0 },
93         { "ambient_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, ambient_blue ), 0, 0, 0, 0 },
94         { "diffuse_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_red ), 0, 0, 0, 0 },
95         { "diffuse_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_green ), 0, 0, 0, 0 },
96         { "diffuse_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, diffuse_blue ), 0, 0, 0, 0 },
97         { "specular_red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_red ), 0, 0, 0, 0 },
98         { "specular_green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_green ), 0, 0, 0, 0 },
99         { "specular_blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, specular_blue ), 0, 0, 0, 0 },
100         { "specular_coeff", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, specular_coeff ), 0, 0, 0, 0 },
101         { "specular_power", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, specular_power ), 0, 0, 0, 0 },
102         { "texture_u", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, texture_u), 0, 0, 0, 0 },
103         { "texture_v", PLY_FLOAT, PLY_FLOAT, offsetof(_Vertex, texture_v), 0, 0, 0, 0 },
104     };
105 
106     // use all 6 properties when reading colors, only the first 3 otherwise
107     for( int i = 0; i < 3; ++i )
108         ply_get_property( file, "vertex", &vertexProps[i] );
109 
110     if (fields & NORMALS)
111       for( int i = 3; i < 6; ++i )
112         ply_get_property( file, "vertex", &vertexProps[i] );
113 
114     if (fields & RGB)
115       for( int i = 6; i < 9; ++i )
116         ply_get_property( file, "vertex", &vertexProps[i] );
117 
118     if (fields & RGBA)
119         ply_get_property( file, "vertex", &vertexProps[9] );
120 
121     if (fields & AMBIENT)
122       for( int i = 10; i < 13; ++i )
123         ply_get_property( file, "vertex", &vertexProps[i] );
124 
125     if (fields & DIFFUSE)
126       for( int i = 13; i < 16; ++i )
127         ply_get_property( file, "vertex", &vertexProps[i] );
128 
129     if (fields & SPECULAR)
130       for( int i = 16; i < 21; ++i )
131         ply_get_property( file, "vertex", &vertexProps[i] );
132 
133     if (fields & TEXCOORD)
134         for (int i = 21; i < 23; ++i)
135             ply_get_property(file, "vertex", &vertexProps[i]);
136 
137     // check whether array is valid otherwise allocate the space
138     if(!_vertices.valid())
139         _vertices = new osg::Vec3Array;
140 
141     if( fields & NORMALS )
142     {
143         if(!_normals.valid())
144             _normals = new osg::Vec3Array;
145     }
146 
147     // If read colors allocate space for color array
148     if( fields & RGB || fields & RGBA)
149     {
150         if(!_colors.valid())
151             _colors = new osg::Vec4Array;
152     }
153 
154     if( fields & AMBIENT )
155     {
156         if(!_ambient.valid())
157             _ambient = new osg::Vec4Array;
158     }
159 
160     if( fields & DIFFUSE )
161     {
162         if(!_diffuse.valid())
163             _diffuse = new osg::Vec4Array;
164     }
165 
166     if( fields & SPECULAR )
167     {
168         if(!_specular.valid())
169             _specular = new osg::Vec4Array;
170     }
171     if (fields & TEXCOORD)
172     {
173         if (!_texcoord.valid())
174             _texcoord = new osg::Vec2Array;
175     }
176 
177     // read in the vertices
178     for( int i = 0; i < nVertices; ++i )
179     {
180         ply_get_element( file, static_cast< void* >( &vertex ) );
181         _vertices->push_back( osg::Vec3( vertex.x, vertex.y, vertex.z ) );
182         if (fields & NORMALS)
183             _normals->push_back( osg::Vec3( vertex.nx, vertex.ny, vertex.nz ) );
184 
185         if( fields & RGBA )
186             _colors->push_back( osg::Vec4( (unsigned int) vertex.red / 255.0,
187                                            (unsigned int) vertex.green / 255.0 ,
188                                            (unsigned int) vertex.blue / 255.0,
189                                            (unsigned int) vertex.alpha / 255.0) );
190         else if( fields & RGB )
191             _colors->push_back( osg::Vec4( (unsigned int) vertex.red / 255.0,
192                                            (unsigned int) vertex.green / 255.0 ,
193                                            (unsigned int) vertex.blue / 255.0, 1.0 ) );
194         if( fields & AMBIENT )
195             _ambient->push_back( osg::Vec4( (unsigned int) vertex.ambient_red / 255.0,
196                                             (unsigned int) vertex.ambient_green / 255.0 ,
197                                             (unsigned int) vertex.ambient_blue / 255.0, 1.0 ) );
198 
199         if( fields & DIFFUSE )
200             _diffuse->push_back( osg::Vec4( (unsigned int) vertex.diffuse_red / 255.0,
201                                             (unsigned int) vertex.diffuse_green / 255.0 ,
202                                             (unsigned int) vertex.diffuse_blue / 255.0, 1.0 ) );
203 
204         if( fields & SPECULAR )
205             _specular->push_back( osg::Vec4( (unsigned int) vertex.specular_red / 255.0,
206                                              (unsigned int) vertex.specular_green / 255.0 ,
207                                              (unsigned int) vertex.specular_blue / 255.0, 1.0 ) );
208         if (fields & TEXCOORD)
209             _texcoord->push_back(osg::Vec2(vertex.texture_u,vertex.texture_v));
210     }
211 }
212 
213 
214 /*  Read the index data from the open file.  */
readTriangles(PlyFile * file,const int nFaces)215 void VertexData::readTriangles( PlyFile* file, const int nFaces )
216 {
217     // temporary face structure for ply loading
218     struct _Face
219     {
220         unsigned char   nVertices;
221         int*            vertices;
222     } face;
223 
224     PlyProperty faceProps[] =
225     {
226         { "vertex_indices|vertex_index", PLY_INT, PLY_INT, offsetof( _Face, vertices ),
227           1, PLY_UCHAR, PLY_UCHAR, offsetof( _Face, nVertices ) }
228     };
229 
230     ply_get_property( file, "face", &faceProps[0] );
231 
232     if(!_triangles.valid())
233         _triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
234 
235     if(!_quads.valid())
236         _quads = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS);
237 
238 
239     const char NUM_VERTICES_TRIANGLE(3);
240     const char NUM_VERTICES_QUAD(4);
241 
242     // read the faces, reversing the reading direction if _invertFaces is true
243     for( int i = 0 ; i < nFaces; i++ )
244     {
245         // initialize face values
246         face.nVertices = 0;
247         face.vertices = 0;
248 
249         ply_get_element( file, static_cast< void* >( &face ) );
250         if (face.vertices)
251         {
252             if (face.nVertices == NUM_VERTICES_TRIANGLE ||  face.nVertices == NUM_VERTICES_QUAD)
253             {
254                 unsigned short index;
255                 for(int j = 0 ; j < face.nVertices ; j++)
256                 {
257                     index = ( _invertFaces ? face.nVertices - 1 - j : j );
258                     if(face.nVertices == 4)
259                         _quads->push_back(face.vertices[index]);
260                     else
261                         _triangles->push_back(face.vertices[index]);
262                 }
263             }
264             // free the memory that was allocated by ply_get_element
265             free( face.vertices );
266         }
267     }
268 }
269 
270 
271 /*  Open a PLY file and read vertex, color and index data. and returns the node  */
readPlyFile(const char * filename,const bool ignoreColors)272 osg::Node* VertexData::readPlyFile( const char* filename, const bool ignoreColors )
273 {
274     int     nPlyElems;
275     char**  elemNames;
276     int     fileType;
277     float   version;
278     bool    result = false;
279     int     nComments;
280     char**  comments;
281 
282     PlyFile* file = NULL;
283 
284     // Try to open ply file as for reading
285     try{
286             file  = ply_open_for_reading( const_cast< char* >( filename ),
287                                           &nPlyElems, &elemNames,
288                                           &fileType, &version );
289     }
290     // Catch the if any exception thrown
291     catch( exception& e )
292     {
293         MESHERROR << "Unable to read PLY file, an exception occurred:  "
294                     << e.what() << endl;
295     }
296 
297     if( !file )
298     {
299         MESHERROR << "Unable to open PLY file " << filename
300                   << " for reading." << endl;
301         return NULL;
302     }
303 
304     MESHASSERT( elemNames != 0 );
305 
306 
307     nComments = file->num_comments;
308     comments = file->comments;
309 
310 
311     #ifndef NDEBUG
312     MESHINFO << filename << ": " << nPlyElems << " elements, file type = "
313              << fileType << ", version = " << version << endl;
314     #endif
315 
316     std::string textureFile;
317     for( int i = 0; i < nComments; i++ )
318     {
319         if( equal_strings( comments[i], "modified by flipply" ) )
320         {
321             _invertFaces = true;
322         }
323         if (strncmp(comments[i], "TextureFile",11)==0)
324         {
325             textureFile = comments[i]+12;
326             if (!osgDB::isAbsolutePath(textureFile))
327             {
328                 textureFile = osgDB::concatPaths(osgDB::getFilePath(filename), textureFile);
329             }
330         }
331     }
332     for( int i = 0; i < nPlyElems; ++i )
333     {
334         int nElems;
335         int nProps;
336 
337         PlyProperty** props = NULL;
338         try{
339                 props = ply_get_element_description( file, elemNames[i],
340                                                      &nElems, &nProps );
341         }
342         catch( exception& e )
343         {
344             MESHERROR << "Unable to get PLY file description, an exception occurred:  "
345                         << e.what() << endl;
346         }
347         MESHASSERT( props != 0 );
348 
349         #ifndef NDEBUG
350         MESHINFO << "element " << i << ": name = " << elemNames[i] << ", "
351                  << nProps << " properties, " << nElems << " elements" << endl;
352         for( int j = 0; j < nProps; ++j )
353         {
354             MESHINFO << "element " << i << ", property " << j << ": "
355                      << "name = " << props[j]->name << endl;
356         }
357         #endif
358 
359         // if the string is vertex means vertex data is started
360         if( equal_strings( elemNames[i], "vertex" ) )
361         {
362             int fields = NONE;
363             // determine if the file stores vertex colors
364             for( int j = 0; j < nProps; ++j )
365             {
366                 // if the string have the red means color info is there
367                 if( equal_strings( props[j]->name, "x" ) )
368                     fields |= XYZ;
369                 if( equal_strings( props[j]->name, "nx" ) )
370                     fields |= NORMALS;
371                 if( equal_strings( props[j]->name, "alpha" ) )
372                     fields |= RGBA;
373                 if ( equal_strings( props[j]->name, "red" ) )
374                     fields |= RGB;
375                 if( equal_strings( props[j]->name, "ambient" ) )
376                     fields |= AMBIENT;
377                 if( equal_strings( props[j]->name, "diffuse_red" ) )
378                     fields |= DIFFUSE;
379                 if (equal_strings(props[j]->name, "specular_red"))
380                     fields |= SPECULAR;
381                 if (equal_strings(props[j]->name, "texture_u"))
382                     fields |= TEXCOORD;
383                 if (equal_strings(props[j]->name, "texture_v"))
384                     fields |= TEXCOORD;
385             }
386 
387             if( ignoreColors )
388             {
389                 fields &= ~(XYZ | NORMALS);
390                     MESHINFO << "Colors in PLY file ignored per request." << endl;
391             }
392 
393             try {
394                 // Read vertices and store in a std::vector array
395                 readVertices( file, nElems, fields );
396                 // Check whether all vertices are loaded or not
397                 MESHASSERT( _vertices->size() == static_cast< size_t >( nElems ) );
398 
399                 // Check if all the optional elements were read or not
400                 if( fields & NORMALS )
401                 {
402                     MESHASSERT( _normals->size() == static_cast< size_t >( nElems ) );
403                 }
404                 if( fields & RGB || fields & RGBA)
405                 {
406                     MESHASSERT( _colors->size() == static_cast< size_t >( nElems ) );
407                 }
408                 if( fields & AMBIENT )
409                 {
410                     MESHASSERT( _ambient->size() == static_cast< size_t >( nElems ) );
411                 }
412                 if( fields & DIFFUSE )
413                 {
414                     MESHASSERT( _diffuse->size() == static_cast< size_t >( nElems ) );
415                 }
416                 if (fields & SPECULAR)
417                 {
418                     MESHASSERT(_specular->size() == static_cast< size_t >(nElems));
419                 }
420                 if (fields & TEXCOORD)
421                 {
422                     MESHASSERT(_texcoord->size() == static_cast< size_t >(nElems));
423                 }
424 
425                 result = true;
426             }
427             catch( exception& e )
428             {
429                 MESHERROR << "Unable to read vertex in PLY file, an exception occurred:  "
430                             << e.what() << endl;
431                 // stop for loop by setting the loop variable to break condition
432                 // this way resources still get released even on error cases
433                 i = nPlyElems;
434 
435             }
436         }
437         // If the string is face means triangle info started
438         else if( equal_strings( elemNames[i], "face" ) )
439         try
440         {
441             // Read Triangles
442             readTriangles( file, nElems );
443             // Check whether all face elements read or not
444 #if DEBUG
445             unsigned int nbTriangles = (_triangles.valid() ? _triangles->size() / 3 : 0) ;
446             unsigned int nbQuads = (_quads.valid() ? _quads->size() / 4 : 0 );
447 
448             MESHASSERT( (nbTriangles + nbQuads) == static_cast< size_t >( nElems ) );
449 #endif
450             result = true;
451         }
452         catch( exception& e )
453         {
454             MESHERROR << "Unable to read PLY file, an exception occurred:  "
455                       << e.what() << endl;
456             // stop for loop by setting the loop variable to break condition
457             // this way resources still get released even on error cases
458             i = nPlyElems;
459         }
460 
461         // free the memory that was allocated by ply_get_element_description
462         for( int j = 0; j < nProps; ++j )
463             free( props[j] );
464         free( props );
465     }
466 
467     ply_close( file );
468 
469     // free the memory that was allocated by ply_open_for_reading
470     for( int i = 0; i < nPlyElems; ++i )
471         free( elemNames[i] );
472     free( elemNames );
473 
474    // If the result is true means the ply file is successfully read
475    if(result)
476    {
477         // Create geometry node
478         osg::Geometry* geom  =  new osg::Geometry;
479 
480         // set the vertex array
481         geom->setVertexArray(_vertices.get());
482 
483         // Add the primitive set
484         bool hasTriOrQuads = false;
485         if (_triangles.valid() && _triangles->size() > 0 )
486         {
487             geom->addPrimitiveSet(_triangles.get());
488             hasTriOrQuads = true;
489         }
490 
491         if (_quads.valid() && _quads->size() > 0 )
492         {
493             geom->addPrimitiveSet(_quads.get());
494             hasTriOrQuads = true;
495         }
496 
497         // Print points if the file contains unsupported primitives
498         if(!hasTriOrQuads)
499             geom->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, _vertices->size()));
500 
501 
502         // Apply the colours to the model; at the moment this is a
503         // kludge because we only use one kind and apply them all the
504         // same way. Also, the priority order is completely arbitrary
505 
506         if(_colors.valid())
507         {
508             geom->setColorArray(_colors.get(), osg::Array::BIND_PER_VERTEX );
509         }
510         else if(_ambient.valid())
511         {
512             geom->setColorArray(_ambient.get(), osg::Array::BIND_PER_VERTEX );
513         }
514         else if(_diffuse.valid())
515         {
516             geom->setColorArray(_diffuse.get(), osg::Array::BIND_PER_VERTEX );
517         }
518         else if(_specular.valid())
519         {
520             geom->setColorArray(_specular.get(), osg::Array::BIND_PER_VERTEX );
521         }
522         else if (_texcoord.valid())
523         {
524             geom->setTexCoordArray(0, _texcoord.get());
525         }
526 
527         // If the model has normals, add them to the geometry
528         if(_normals.valid())
529         {
530             geom->setNormalArray(_normals.get(), osg::Array::BIND_PER_VERTEX);
531         }
532         else
533         {   // If not, use the smoothing visitor to generate them
534             // (quads will be triangulated by the smoothing visitor)
535             osgUtil::SmoothingVisitor::smooth((*geom), osg::PI/2);
536         }
537 
538         // set flage true to activate the vertex buffer object of drawable
539         geom->setUseVertexBufferObjects(true);
540 
541         osg::ref_ptr<osg::Image> image;
542         if (!textureFile.empty() && (image = osgDB::readRefImageFile(textureFile)) != NULL)
543         {
544             osg::Texture2D *texture = new osg::Texture2D;
545             texture->setImage(image.get());
546             texture->setResizeNonPowerOfTwoHint(false);
547 
548             osg::TexEnv *texenv = new osg::TexEnv;
549             texenv->setMode(osg::TexEnv::REPLACE);
550 
551             osg::StateSet *stateset = geom->getOrCreateStateSet();
552             stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
553             stateset->setTextureAttribute(0, texenv);
554         }
555 
556         osg::Geode* geode = new osg::Geode;
557         geode->addDrawable(geom);
558         return geode;
559     }
560 
561     return NULL;
562 }
563 
564 
565