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