Equalizer  1.6.1
seqPly/vertexData.cpp
1 
2 /* Copyright (c) 2007, Tobias Wolf <twolf@access.unizh.ch>
3  * 2009-2012, Stefan Eilemann <eile@equalizergraphics.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * - Neither the name of Eyescale Software GmbH nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include "vertexData.h"
31 #include "ply.h"
32 
33 #include <cstdlib>
34 #include <algorithm>
35 
36 #if (( __GNUC__ > 4 ) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) )
37 # include <parallel/algorithm>
38 using __gnu_parallel::sort;
39 #else
40 using std::sort;
41 #endif
42 
43 using namespace mesh;
44 
45 /* Contructor. */
46 VertexData::VertexData()
47  : _invertFaces( false )
48 {
49  _boundingBox[0] = Vertex( 0.0f );
50  _boundingBox[1] = Vertex( 0.0f );
51 }
52 
53 
54 /* Read the vertex and (if available/wanted) color data from the open file. */
55 void VertexData::readVertices( PlyFile* file, const int nVertices,
56  const bool readColors )
57 {
58  // temporary vertex structure for ply loading
59  struct _Vertex
60  {
61  float x;
62  float y;
63  float z;
64  unsigned char r;
65  unsigned char g;
66  unsigned char b;
67  } vertex;
68 
69  PlyProperty vertexProps[] =
70  {
71  { "x", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, x ), 0, 0, 0, 0 },
72  { "y", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, y ), 0, 0, 0, 0 },
73  { "z", PLY_FLOAT, PLY_FLOAT, offsetof( _Vertex, z ), 0, 0, 0, 0 },
74  { "red", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, r ), 0, 0, 0, 0 },
75  { "green", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, g ), 0, 0, 0, 0 },
76  { "blue", PLY_UCHAR, PLY_UCHAR, offsetof( _Vertex, b ), 0, 0, 0, 0 }
77  };
78 
79  // use all 6 properties when reading colors, only the first 3 otherwise
80  int limit = readColors ? 6 : 3;
81  for( int i = 0; i < limit; ++i )
82  ply_get_property( file, "vertex", &vertexProps[i] );
83 
84  vertices.clear();
85  vertices.reserve( nVertices );
86 
87  if( readColors )
88  {
89  colors.clear();
90  colors.reserve( nVertices );
91  }
92 
93  // read in the vertices
94  for( int i = 0; i < nVertices; ++i )
95  {
96  ply_get_element( file, static_cast< void* >( &vertex ) );
97  vertices.push_back( Vertex( vertex.x, vertex.y, vertex.z ) );
98  if( readColors )
99  colors.push_back( Color( vertex.r, vertex.g, vertex.b ) );
100  }
101 }
102 
103 
104 /* Read the index data from the open file. */
105 void VertexData::readTriangles( PlyFile* file, const int nFaces )
106 {
107  // temporary face structure for ply loading
108  struct _Face
109  {
110  unsigned char nVertices;
111  int* vertices;
112  } face;
113 
114  PlyProperty faceProps[] =
115  {
116  { "vertex_indices", PLY_INT, PLY_INT, offsetof( _Face, vertices ),
117  1, PLY_UCHAR, PLY_UCHAR, offsetof( _Face, nVertices ) }
118  };
119 
120  ply_get_property( file, "face", &faceProps[0] );
121 
122  triangles.clear();
123  triangles.reserve( nFaces );
124 
125  // read in the faces, asserting that they are only triangles
126  uint8_t ind1 = _invertFaces ? 2 : 0;
127  uint8_t ind3 = _invertFaces ? 0 : 2;
128  for( int i = 0; i < nFaces; ++i )
129  {
130  ply_get_element( file, static_cast< void* >( &face ) );
131  MESHASSERT( face.vertices != 0 );
132  if( face.nVertices != 3 )
133  {
134  free( face.vertices );
135  throw MeshException( "Error reading PLY file. Encountered a "
136  "face which does not have three vertices." );
137  }
138  triangles.push_back( Triangle( face.vertices[ind1],
139  face.vertices[1],
140  face.vertices[ind3] ) );
141 
142  // free the memory that was allocated by ply_get_element
143  free( face.vertices );
144  }
145 }
146 
147 
148 /* Open a PLY file and read vertex, color and index data. */
149 bool VertexData::readPlyFile( const std::string& filename )
150 {
151  int nPlyElems;
152  char** elemNames;
153  int fileType;
154  float version;
155  bool result = false;
156 
157  PlyFile* file = ply_open_for_reading( const_cast<char*>( filename.c_str( )),
158  &nPlyElems, &elemNames,
159  &fileType, &version );
160  if( !file )
161  {
162  MESHERROR << "Unable to open PLY file " << filename
163  << " for reading." << std::endl;
164  return result;
165  }
166  MESHASSERT( elemNames != 0 );
167 
168  #ifndef NDEBUG
169  MESHINFO << filename << ": " << nPlyElems << " elements, file type = "
170  << fileType << ", version = " << version << std::endl;
171  #endif
172 
173  for( int i = 0; i < nPlyElems; ++i )
174  {
175  int nElems;
176  int nProps;
177 
178  PlyProperty** props = ply_get_element_description( file, elemNames[i],
179  &nElems, &nProps );
180  MESHASSERT( props != 0 );
181 
182  #ifndef NDEBUG
183  MESHINFO << "element " << i << ": name = " << elemNames[i] << ", "
184  << nProps << " properties, " << nElems << " elements"
185  << std::endl;
186  for( int j = 0; j < nProps; ++j )
187  {
188  MESHINFO << "element " << i << ", property " << j << ": "
189  << "name = " << props[j]->name << std::endl;
190  }
191  #endif
192 
193  if( equal_strings( elemNames[i], "vertex" ) )
194  {
195  bool hasColors = false;
196  // determine if the file stores vertex colors
197  for( int j = 0; j < nProps; ++j )
198  if( equal_strings( props[j]->name, "red" ) )
199  hasColors = true;
200 
201  readVertices( file, nElems, hasColors );
202  MESHASSERT( vertices.size() == static_cast< size_t >( nElems ) );
203  if( hasColors )
204  {
205  MESHASSERT( colors.size() == static_cast< size_t >( nElems ));
206  }
207  }
208  else if( equal_strings( elemNames[i], "face" ) )
209  try
210  {
211  readTriangles( file, nElems );
212  MESHASSERT( triangles.size() == static_cast< size_t >( nElems ) );
213  result = true;
214  }
215  catch( const std::exception& e )
216  {
217  MESHERROR << "Unable to read PLY file, an exception occured: "
218  << e.what() << std::endl;
219  // stop for loop by setting the loop variable to break condition
220  // this way resources still get released even on error cases
221  i = nPlyElems;
222  }
223 
224  // free the memory that was allocated by ply_get_element_description
225  for( int j = 0; j < nProps; ++j )
226  free( props[j] );
227  free( props );
228  }
229 
230  ply_close( file );
231 
232  // free the memory that was allocated by ply_open_for_reading
233  for( int i = 0; i < nPlyElems; ++i )
234  free( elemNames[i] );
235  free( elemNames );
236 
237  return result;
238 }
239 
240 
241 /* Calculate the face or vertex normals of the current vertex data. */
242 void VertexData::calculateNormals()
243 {
244 #ifndef NDEBUG
245  int wrongNormals = 0;
246 #endif
247 
248  normals.clear();
249  normals.reserve( vertices.size() );
250 
251  // initialize all normals to zero
252  for( size_t i = 0; i < vertices.size(); ++i )
253  normals.push_back( Normal( 0, 0, 0 ) );
254 
255  // iterate over all triangles and add their normals to adjacent vertices
256 #pragma omp parallel for
257  for( ssize_t i = 0; i < ssize_t( triangles.size( )); ++i )
258  {
259  const Index i0 = triangles[i][0];
260  const Index i1 = triangles[i][1];
261  const Index i2 = triangles[i][2];
262  const Normal normal = vertices[i0].compute_normal( vertices[i1],
263  vertices[i2] );
264 #ifndef NDEBUG
265  // count emtpy normals in debug mode
266  if( normal.length() == 0.0f )
267  ++wrongNormals; // racy with OpenMP, but not critical
268 #endif
269 
270  normals[i0] += normal;
271  normals[i1] += normal;
272  normals[i2] += normal;
273  }
274 
275  // normalize all the normals
276 #pragma omp parallel for
277  for( ssize_t i = 0; i < ssize_t( vertices.size( )); ++i )
278  normals[i].normalize();
279 
280 #ifndef NDEBUG
281  if( wrongNormals > 0 )
282  MESHINFO << wrongNormals << " faces have no valid normal." << std::endl;
283 #endif
284 }
285 
286 
287 /* Calculate the bounding box of the current vertex data. */
288 void VertexData::calculateBoundingBox()
289 {
290  _boundingBox[0] = vertices[0];
291  _boundingBox[1] = vertices[0];
292  for( size_t v = 1; v < vertices.size(); ++v )
293  for( size_t i = 0; i < 3; ++i )
294  {
295  _boundingBox[0][i] = std::min( _boundingBox[0][i], vertices[v][i] );
296  _boundingBox[1][i] = std::max( _boundingBox[1][i], vertices[v][i] );
297  }
298 }
299 
300 
301 /* Calculates longest axis for a set of triangles */
302 Axis VertexData::getLongestAxis( const size_t start,
303  const size_t elements ) const
304 {
305  if( start + elements > triangles.size() )
306  {
307  LBERROR << "incorrect request to getLongestAxis" << std::endl
308  << "start: " << start << std::endl
309  << "elements: " << elements << std::endl
310  << "sum: " << start+elements << std::endl
311  << "data size: " << triangles.size() << std::endl;
312  return AXIS_X;
313  }
314 
315  BoundingBox bb;
316  bb[0] = vertices[ triangles[start][0] ];
317  bb[1] = vertices[ triangles[start][0] ];
318 
319  for( size_t t = start; t < start+elements; ++t )
320  for( size_t v = 0; v < 3; ++v )
321  for( size_t i = 0; i < 3; ++i )
322  {
323  bb[0][i] = std::min( bb[0][i], vertices[ triangles[t][v] ][i] );
324  bb[1][i] = std::max( bb[1][i], vertices[ triangles[t][v] ][i] );
325  }
326 
327  const GLfloat bbX = bb[1][0] - bb[0][0];
328  const GLfloat bbY = bb[1][1] - bb[0][1];
329  const GLfloat bbZ = bb[1][2] - bb[0][2];
330 
331  if( bbX >= bbY && bbX >= bbZ )
332  return AXIS_X;
333 
334  if( bbY >= bbX && bbY >= bbZ )
335  return AXIS_Y;
336 
337  return AXIS_Z;
338 }
339 
340 
341 /* Scales the data to be within +- baseSize/2 (default 2.0) coordinates. */
342 void VertexData::scale( const float baseSize )
343 {
344  // calculate bounding box if not yet done
345  if( _boundingBox[0].length() == 0.0f && _boundingBox[1].length() == 0.0f )
346  calculateBoundingBox();
347 
348  // find largest dimension and determine scale factor
349  float factor = 0.0f;
350  for( size_t i = 0; i < 3; ++i )
351  factor = std::max( factor, _boundingBox[1][i] - _boundingBox[0][i] );
352  factor = baseSize / factor;
353 
354  // determine scale offset
355  Vertex offset;
356  for( size_t i = 0; i < 3; ++i )
357  offset[i] = ( _boundingBox[0][i] + _boundingBox[1][i] ) * 0.5f;
358 
359  // scale the data
360 #pragma omp parallel for
361  for( ssize_t v = 0; v < ssize_t( vertices.size( )); ++v )
362  for( size_t i = 0; i < 3; ++i )
363  {
364  vertices[v][i] -= offset[i];
365  vertices[v][i] *= factor;
366  }
367 
368  // scale the bounding box
369  for( size_t v = 0; v < 2; ++v )
370  for( size_t i = 0; i < 3; ++i )
371  {
372  _boundingBox[v][i] -= offset[i];
373  _boundingBox[v][i] *= factor;
374  }
375 }
376 
377 
379 /* Helper structure to sort Triangles with standard library sort function. */
380 struct _TriangleSort
381 {
382  _TriangleSort( const VertexData& data, const Axis axis ) : _data( data ),
383  _axis( axis ) {}
384 
385  bool operator() ( const Triangle& t1, const Triangle& t2 )
386  {
387  // references to both triangles' three vertices
388  const Vertex& v11 = _data.vertices[ t1[0] ];
389  const Vertex& v12 = _data.vertices[ t1[1] ];
390  const Vertex& v13 = _data.vertices[ t1[2] ];
391  const Vertex& v21 = _data.vertices[ t2[0] ];
392  const Vertex& v22 = _data.vertices[ t2[1] ];
393  const Vertex& v23 = _data.vertices[ t2[2] ];
394 
395  // compare first by given axis
396  int axis = _axis;
397  do
398  {
399  // test median of 'axis' component of all three vertices
400  const float median1 = (v11[axis] + v12[axis] + v13[axis] ) / 3.0f;
401  const float median2 = (v21[axis] + v22[axis] + v23[axis] ) / 3.0f;
402  if( median1 != median2 )
403  return ( median1 < median2 );
404 
405  // if still equal, move on to the next axis
406  axis = ( axis + 1 ) % 3;
407  }
408  while( axis != _axis );
409 
410  return false;
411  }
412 
413  const VertexData& _data;
414  const Axis _axis;
415 };
418 /* Sort the index data from start to start + length along the given axis. */
419 void VertexData::sort( const Index start, const Index length, const Axis axis )
420 {
421  MESHASSERT( length > 0 );
422  MESHASSERT( start + length <= triangles.size() );
423 
424  ::sort( triangles.begin() + start, triangles.begin() + start + length,
425  _TriangleSort( *this, axis ) );
426 }