Equalizer  1.6.1
eqPly/vertexBufferRoot.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 
31 #include "vertexBufferRoot.h"
32 #include "vertexBufferState.h"
33 #include "vertexData.h"
34 #include <string>
35 #include <sstream>
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #ifndef _WIN32
40 # include <sys/mman.h>
41 #endif
42 
43 namespace mesh
44 {
45 
46 typedef vmml::frustum_culler< float > FrustumCuller;
47 
48 /* Determine number of bits used by the current architecture. */
49 size_t getArchitectureBits();
50 /* Determine whether the current architecture is little endian or not. */
51 bool isArchitectureLittleEndian();
52 /* Construct architecture dependent file name. */
53 std::string getArchitectureFilename( const std::string& filename );
54 
55 /* Begin kd-tree setup, go through full range starting with x axis. */
56 void VertexBufferRoot::setupTree( VertexData& data )
57 {
58  // data is VertexData, _data is VertexBufferData
59  _data.clear();
60 
61  const Axis axis = data.getLongestAxis( 0, data.triangles.size() );
62 
63  VertexBufferNode::setupTree( data, 0, data.triangles.size(),
64  axis, 0, _data );
65  VertexBufferNode::updateBoundingSphere();
66  VertexBufferNode::updateRange();
67 
68 #if 0
69  // re-test all points to be in the bounding sphere
70  Vertex center( _boundingSphere.array );
71  float radius = _boundingSphere.w();
72  float radiusSquared = radius * radius;
73  for( size_t offset = 0; offset < _data.vertices.size(); ++offset )
74  {
75  const Vertex& vertex = _data.vertices[ offset ];
76 
77  const Vertex centerToPoint = vertex - center;
78  const float distanceSquared = centerToPoint.squared_length();
79  LBASSERTINFO( distanceSquared <= radiusSquared,
80  distanceSquared << " > " << radiusSquared );
81  }
82 #endif
83 }
84 
85 // #define LOGCULL
86 void VertexBufferRoot::cullDraw( VertexBufferState& state ) const
87 {
88  _beginRendering( state );
89 
90 #ifdef LOGCULL
91  size_t verticesRendered = 0;
92  size_t verticesOverlap = 0;
93 #endif
94 
95  const Range& range = state.getRange();
96  FrustumCuller culler;
97  culler.setup( state.getProjectionModelViewMatrix( ));
98 
99  // start with root node
100  std::vector< const mesh::VertexBufferBase* > candidates;
101  candidates.push_back( this );
102 
103  while( !candidates.empty() )
104  {
105  if( state.stopRendering( ))
106  return;
107 
108  const mesh::VertexBufferBase* treeNode = candidates.back();
109  candidates.pop_back();
110 
111  // completely out of range check
112  if( treeNode->getRange()[0] >= range[1] ||
113  treeNode->getRange()[1] < range[0] )
114  {
115  continue;
116  }
117 
118  // bounding sphere view frustum culling
119  const vmml::Visibility visibility = state.useFrustumCulling() ?
120  culler.test_sphere( treeNode->getBoundingSphere( )) :
121  vmml::VISIBILITY_FULL;
122  switch( visibility )
123  {
124  case vmml::VISIBILITY_FULL:
125  // if fully visible and fully in range, render it
126  if( treeNode->getRange()[0] >= range[0] &&
127  treeNode->getRange()[1] < range[1] )
128  {
129  treeNode->draw( state );
130  //treeNode->drawBoundingSphere( state );
131 #ifdef LOGCULL
132  verticesRendered += treeNode->getNumberOfVertices();
133 #endif
134  break;
135  }
136  // partial range, fall through to partial visibility
137 
138  case vmml::VISIBILITY_PARTIAL:
139  {
140  const mesh::VertexBufferBase* left = treeNode->getLeft();
141  const mesh::VertexBufferBase* right = treeNode->getRight();
142 
143  if( !left && !right )
144  {
145  if( treeNode->getRange()[0] >= range[0] )
146  {
147  treeNode->draw( state );
148  //treeNode->drawBoundingSphere( state );
149 #ifdef LOGCULL
150  verticesRendered += treeNode->getNumberOfVertices();
151  if( visibility == vmml::VISIBILITY_PARTIAL )
152  verticesOverlap += treeNode->getNumberOfVertices();
153 #endif
154  }
155  // else drop, to be drawn by 'previous' channel
156  }
157  else
158  {
159  if( left )
160  candidates.push_back( left );
161  if( right )
162  candidates.push_back( right );
163  }
164  break;
165  }
166  case vmml::VISIBILITY_NONE:
167  // do nothing
168  break;
169  }
170  }
171 
172  _endRendering( state );
173 
174 #ifdef LOGCULL
175  const size_t verticesTotal = model->getNumberOfVertices();
176  MESHINFO
177  << getName() << " rendered " << verticesRendered * 100 / verticesTotal
178  << "% of model, overlap <= " << verticesOverlap * 100 / verticesTotal
179  << "%" << std::endl;
180 #endif
181 }
182 
183 
184 /* Set up the common OpenGL state for rendering of all nodes. */
185 void VertexBufferRoot::_beginRendering( VertexBufferState& state ) const
186 {
187  state.resetRegion();
188  switch( state.getRenderMode() )
189  {
190 #ifdef GL_ARB_vertex_buffer_object
191  case RENDER_MODE_BUFFER_OBJECT:
192  glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
193  glEnableClientState( GL_VERTEX_ARRAY );
194  glEnableClientState( GL_NORMAL_ARRAY );
195  if( state.useColors() )
196  glEnableClientState( GL_COLOR_ARRAY );
197 #endif
198  case RENDER_MODE_DISPLAY_LIST:
199  case RENDER_MODE_IMMEDIATE:
200  default:
201  ;
202  }
203 }
204 
205 
206 /* Delegate rendering to node routine. */
207 void VertexBufferRoot::draw( VertexBufferState& state ) const
208 {
209  VertexBufferNode::draw( state );
210 }
211 
212 
213 /* Tear down the common OpenGL state for rendering of all nodes. */
214 void VertexBufferRoot::_endRendering( VertexBufferState& state ) const
215 {
216  switch( state.getRenderMode() )
217  {
218 #ifdef GL_ARB_vertex_buffer_object
219  case RENDER_MODE_BUFFER_OBJECT:
220  {
221  // deactivate VBO and EBO use
222 #define glewGetContext state.glewGetContext
223  glBindBuffer( GL_ARRAY_BUFFER_ARB, 0);
224  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
225  glPopClientAttrib();
226  }
227 #endif
228  case RENDER_MODE_DISPLAY_LIST:
229  case RENDER_MODE_IMMEDIATE:
230  default:
231  ;
232  }
233 }
234 
235 
236 /* Determine number of bits used by the current architecture. */
237 size_t getArchitectureBits()
238 {
239  return ( sizeof( void* ) * 8 );
240 }
241 
242 
243 /* Determine whether the current architecture is little endian or not. */
244 bool isArchitectureLittleEndian()
245 {
246  unsigned char test[2] = { 1, 0 };
247  short* x = reinterpret_cast< short* >( test );
248  return ( *x == 1 );
249 }
250 
251 
252 /* Construct architecture dependent file name. */
253 std::string getArchitectureFilename( const std::string& filename )
254 {
255  std::ostringstream oss;
256  oss << filename << ( isArchitectureLittleEndian() ? ".le" : ".be" );
257  oss << getArchitectureBits() << ".bin";
258  return oss.str();
259 }
260 
261 
262 /* Functions extracted out of readFromFile to enhance readability. */
263 bool VertexBufferRoot::_constructFromPly( const std::string& filename )
264 {
265  MESHINFO << "Constructing new from PLY file." << std::endl;
266 
267  VertexData data;
268  if( _invertFaces )
269  data.useInvertedFaces();
270  if( !data.readPlyFile( filename ) )
271  {
272  MESHERROR << "Unable to load PLY file." << std::endl;
273  return false;
274  }
275 
276  data.calculateNormals();
277  data.scale( 2.0f );
278  setupTree( data );
279  if( !writeToFile( filename ))
280  MESHWARN << "Unable to write binary representation." << std::endl;
281 
282  return true;
283 }
284 
285 bool VertexBufferRoot::_readBinary( std::string filename )
286 {
287 #ifdef WIN32
288 
289  // replace dir delimiters since '\' is often used as escape char
290  for( size_t i=0; i<filename.length(); ++i )
291  if( filename[i] == '\\' )
292  filename[i] = '/';
293 
294  // try to open binary file
295  HANDLE file = CreateFile( filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
296  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
297  if( file == INVALID_HANDLE_VALUE )
298  return false;
299 
300  MESHINFO << "Reading cached binary representation." << std::endl;
301 
302  // create a file mapping
303  HANDLE map = CreateFileMapping( file, 0, PAGE_READONLY, 0, 0,
304  filename.c_str( ));
305  CloseHandle( file );
306  if( !map )
307  {
308  MESHERROR << "Unable to read binary file, file mapping failed."
309  << std::endl;
310  return false;
311  }
312 
313  // get a view of the mapping
314  char* addr = static_cast< char* >( MapViewOfFile( map, FILE_MAP_READ, 0,
315  0, 0 ) );
316  bool result = false;
317 
318  if( addr )
319  {
320  try
321  {
322  fromMemory( addr );
323  result = true;
324  }
325  catch( const std::exception& e )
326  {
327  MESHERROR << "Unable to read binary file, an exception occured: "
328  << e.what() << std::endl;
329  }
330  UnmapViewOfFile( addr );
331  }
332  else
333  {
334  MESHERROR << "Unable to read binary file, memory mapping failed."
335  << std::endl;
336  return false;
337  }
338 
339  CloseHandle( map );
340  return result;
341 
342 #else
343  // try to open binary file
344  int fd = open( filename.c_str(), O_RDONLY );
345  if( fd < 0 )
346  return false;
347 
348  // retrieving file information
349  struct stat status;
350  fstat( fd, &status );
351 
352  // create memory mapped file
353  char* addr = static_cast< char* >( mmap( 0, status.st_size, PROT_READ,
354  MAP_SHARED, fd, 0 ) );
355  bool result = false;
356  if( addr != MAP_FAILED )
357  {
358  try
359  {
360  fromMemory( addr );
361  result = true;
362  }
363  catch( const std::exception& e )
364  {
365  MESHERROR << "Unable to read binary file, an exception occured: "
366  << e.what() << std::endl;
367  }
368  munmap( addr, status.st_size );
369  }
370  else
371  {
372  MESHERROR << "Unable to read binary file, memory mapping failed."
373  << std::endl;
374  }
375 
376  close( fd );
377  return result;
378 #endif
379 }
380 
381 /* Read binary kd-tree representation, construct from ply if unavailable. */
382 bool VertexBufferRoot::readFromFile( const std::string& filename )
383 {
384  if( _readBinary( getArchitectureFilename( filename )))
385  {
386  _name = filename;
387  return true;
388  }
389  if( _constructFromPly( filename ))
390  {
391  _name = filename;
392  return true;
393  }
394  return false;
395 }
396 
397 /* Write binary representation of the kd-tree to file. */
398 bool VertexBufferRoot::writeToFile( const std::string& filename )
399 {
400  bool result = false;
401 
402  std::ofstream output( getArchitectureFilename( filename ).c_str(),
403  std::ios::out | std::ios::binary );
404  if( output )
405  {
406  // enable exceptions on stream errors
407  output.exceptions( std::ofstream::failbit | std::ofstream::badbit );
408  try
409  {
410  toStream( output );
411  result = true;
412  }
413  catch( const std::exception& e )
414  {
415  MESHERROR << "Unable to write binary file, an exception "
416  << "occured: " << e.what() << std::endl;
417  }
418  output.close();
419  }
420  else
421  {
422  MESHERROR << "Unable to create binary file." << std::endl;
423  }
424 
425  return result;
426 }
427 
428 
429 /* Read root node from memory and continue with other nodes. */
430 void VertexBufferRoot::fromMemory( char* start )
431 {
432  char** addr = &start;
433  size_t version;
434  memRead( reinterpret_cast< char* >( &version ), addr, sizeof( size_t ) );
435  if( version != FILE_VERSION )
436  throw MeshException( "Error reading binary file. Version in file "
437  "does not match the expected version." );
438  size_t nodeType;
439  memRead( reinterpret_cast< char* >( &nodeType ), addr, sizeof( size_t ) );
440  if( nodeType != ROOT_TYPE )
441  throw MeshException( "Error reading binary file. Expected the root "
442  "node, but found something else instead." );
443  _data.fromMemory( addr );
444  VertexBufferNode::fromMemory( addr, _data );
445 }
446 
447 
448 /* Write root node to output stream and continue with other nodes. */
449 void VertexBufferRoot::toStream( std:: ostream& os )
450 {
451  size_t version = FILE_VERSION;
452  os.write( reinterpret_cast< char* >( &version ), sizeof( size_t ) );
453  size_t nodeType = ROOT_TYPE;
454  os.write( reinterpret_cast< char* >( &nodeType ), sizeof( size_t ) );
455  _data.toStream( os );
456  VertexBufferNode::toStream( os );
457 }
458 
459 }