Equalizer  1.6.1
rawVolModel.cpp
1 
2 /* Copyright (c) 2007-2011, Maxim Makhinya <maxmah@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  * - Neither the name of Eyescale Software GmbH nor the names of its
13  * contributors may be used to endorse or promote products derived from this
14  * software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "rawVolModel.h"
30 #include "hlp.h"
31 
32 namespace eVolve
33 {
34 
35 
36 using hlpFuncs::clip;
37 using hlpFuncs::hFile;
38 
39 
40 static GLuint createPreintegrationTable( const uint8_t* Table );
41 
42 static bool readTransferFunction( FILE* file, std::vector<uint8_t>& TF );
43 
44 static bool readDimensionsAndScaling
45 (
46  FILE* file,
47  uint32_t& w, uint32_t& h, uint32_t& d,
48  VolumeScaling& volScaling
49 );
50 
51 
52 // Read volume dimensions, scaling and transfer function
53 RawVolumeModel::RawVolumeModel( const std::string& filename )
54  : _headerLoaded( false )
55  , _filename( filename )
56  , _preintName ( 0 )
57  , _w( 0 )
58  , _h( 0 )
59  , _d( 0 )
60  , _tW( 0 )
61  , _tH( 0 )
62  , _tD( 0 )
63  , _hasDerivatives( true )
64  , _glewContext( 0 )
65 {}
66 
67 bool RawVolumeModel::loadHeader( const float brightness, const float alpha )
68 {
69  LBASSERT( !_headerLoaded );
70  LBASSERT( brightness > 0.0f );
71  LBASSERT( alpha > 0.0f );
72  LBLOG( eq::LOG_CUSTOM ) << "-------------------------------------------"
73  << std::endl << "model: " << _filename;
74 
75  hFile header( fopen( ( _filename + std::string( ".vhf" ) ).c_str(), "rb" ));
76 
77  if( header.f==0 )
78  {
79  LBERROR << "Can't open header file" << std::endl;
80  return false;
81  }
82 
83  // test for raw+der or raw
84  const size_t fNameLen = _filename.length();
85  _hasDerivatives =
86  ( fNameLen >= 6 && _filename.substr( fNameLen-6, 6 ) == "_d.raw" ) ?
87  true : false;
88 
89  if( !readDimensionsAndScaling( header.f, _w, _h, _d, _volScaling ) )
90  return false;
91 
92  _resolution = LB_MAX( _w, LB_MAX( _h, _d ) );
93 
94  if( !readTransferFunction( header.f, _TF ))
95  return false;
96 
97  _headerLoaded = true;
98 
99  if( brightness != 1.0f )
100  {
101  for( size_t i = 0; i < _TF.size(); i+=4 )
102  {
103  _TF[i+0] = static_cast< uint8_t >( _TF[i+0] * brightness );
104  _TF[i+1] = static_cast< uint8_t >( _TF[i+1] * brightness );
105  _TF[i+2] = static_cast< uint8_t >( _TF[i+2] * brightness );
106  }
107  }
108 
109  if( alpha != 1.0f )
110  for( size_t i = 3; i < _TF.size(); i+=4 )
111  _TF[i] = static_cast< uint8_t >( _TF[i] * alpha );
112 
113  return true;
114 }
115 
116 
117 static int32_t calcHashKey( const eq::Range& range )
118 {
119  return static_cast<int32_t>(( range.start*10000.f + range.end )*10000.f );
120 }
121 
122 
123 bool RawVolumeModel::getVolumeInfo( VolumeInfo& info, const eq::Range& range )
124 {
125  if( !_headerLoaded && !loadHeader( 1.0f, 1.0f ))
126  return false;
127 
128  if( _preintName == 0 )
129  {
130  LBLOG( eq::LOG_CUSTOM ) << "Creating preint" << std::endl;
131  _preintName = createPreintegrationTable( &_TF[0] );
132  }
133 
134  VolumePart* volumePart = 0;
135  const int32_t key = calcHashKey( range );
136 
137  if( _volumeHash.find( key ) == _volumeHash.end( ) )
138  {
139  // new key
140  volumePart = &_volumeHash[ key ];
141  if( !_createVolumeTexture( volumePart->volume, volumePart->TD, range ))
142  return false;
143  }
144  else
145  { // old key
146  volumePart = &_volumeHash[ key ];
147  }
148 
149  info.volume = volumePart->volume;
150  info.TD = volumePart->TD;
151  info.preint = _preintName;
152  info.volScaling = _volScaling;
153  info.hasDerivatives = _hasDerivatives;
154  if( _hasDerivatives )
155  {
156  info.voxelSize.W = 1.f;
157  info.voxelSize.H = 1.f;
158  info.voxelSize.D = 1.f;
159  }else
160  {
161  info.voxelSize.W = 1.f / _tW;
162  info.voxelSize.H = 1.f / _tH;
163  info.voxelSize.D = 1.f / _tD;
164  }
165  return true;
166 }
167 
168 
169 void RawVolumeModel::releaseVolumeInfo( const eq::Range& range )
170 {
171  const int32_t key = calcHashKey( range );
172  if( _volumeHash.find( key ) == _volumeHash.end() )
173  return;
174 
175  _volumeHash.erase( key );
176 }
177 
178 
181 static uint32_t calcMinPow2( uint32_t size )
182 {
183  if( size == 0 )
184  return 0;
185 
186  size--;
187  uint32_t res = 1;
188 
189  while( size > 0 )
190  {
191  res <<= 1;
192  size >>= 1;
193  }
194  return res;
195 }
196 
197 
200 bool RawVolumeModel::_createVolumeTexture( GLuint& volume,
202  const eq::Range& range )
203 {
204  const uint32_t w = _w;
205  const uint32_t h = _h;
206  const uint32_t d = _d;
207  const uint32_t bytes = _hasDerivatives ? 4 : 1;
208 
209  const int32_t bwStart = 2; //border width from left
210  const int32_t bwEnd = 2; //border width from right
211 
212  const int32_t s =
213  clip<int32_t>( static_cast< int32_t >( d*range.start ), 0, d-1 );
214 
215  const int32_t e =
216  clip<int32_t>( static_cast< int32_t >( d*range.end-1 ), 0, d-1 );
217 
218  const uint32_t start =
219  static_cast<uint32_t>( clip<int32_t>( s-bwStart, 0, d-1 ) );
220 
221  const uint32_t end =
222  static_cast<uint32_t>( clip<int32_t>( e+bwEnd , 0, d-1 ) );
223 
224  const uint32_t depth = end-start+1;
225 
226  _tW = calcMinPow2( w );
227  _tH = calcMinPow2( h );
228  _tD = calcMinPow2( depth );
229 
230  //texture scaling coefficients
231  TD.W = static_cast<float>( w ) / static_cast<float>( _tW );
232  TD.H = static_cast<float>( h ) / static_cast<float>( _tH );
233  TD.D = static_cast<float>( e-s+1 ) / static_cast<float>( _tD );
234  TD.D /= range.end>range.start ? (range.end-range.start) : 1.0f;
235 
236  // Shift coefficient and left border in texture for depth
237  TD.Do = range.start;
238  TD.Db = range.start > 0.0001 ? bwStart / static_cast<float>(_tD) : 0;
239 
240  LBLOG( eq::LOG_CUSTOM )
241  << "===============================================" << std::endl
242  << " w: " << w << " " << _tW
243  << " h: " << h << " " << _tH
244  << " d: " << d << " " << depth << " " << _tD << std::endl
245  << " r: " << _resolution << std::endl
246  << " ws: " << TD.W << " hs: " << TD.H << " wd: " << TD.D
247  << " Do: " << TD.Do << " Db: " << TD.Db << std::endl
248  << " s= " << start << " e= " << end << std::endl;
249 
250  // Reading of requested part of a volume
251  std::vector<uint8_t> data( _tW*_tH*_tD*bytes, 0 );
252  const uint32_t wh4 = w * h * bytes;
253  const uint32_t tWH4 = _tW * _tH * bytes;
254 
255  std::ifstream file ( _filename.c_str(), std::ifstream::in |
256  std::ifstream::binary | std::ifstream::ate );
257 
258  if( !file.is_open() )
259  {
260  LBERROR << "Can't open model data file";
261  return false;
262  }
263 
264  file.seekg( wh4*start, std::ios::beg );
265 
266  if( w==_tW && h==_tH ) // width and height are power of 2
267  {
268  file.read( (char*)( &data[0] ), wh4*depth );
269  }
270  else if( w==_tW ) // only width is power of 2
271  {
272  for( uint32_t i=0; i<depth; i++ )
273  file.read( (char*)( &data[i*tWH4] ), wh4 );
274  }
275  else
276  { // nor width nor heigh is power of 2
277  const uint32_t w4 = w * bytes;
278  const uint32_t tW4 = _tW * bytes;
279 
280  for( uint32_t i=0; i<depth; i++ )
281  for( uint32_t j=0; j<h; j++ )
282  file.read( (char*)( &data[ i*tWH4 + j*tW4] ), w4 );
283  }
284 
285  file.close();
286 
287  LBASSERT( _glewContext );
288  // create 3D texture
289  glGenTextures( 1, &volume );
290  LBLOG( eq::LOG_CUSTOM ) << "generated texture: " << volume << std::endl;
291  glBindTexture(GL_TEXTURE_3D, volume);
292 
293  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE );
294  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE );
295  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R , GL_CLAMP_TO_EDGE );
296  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
297  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
298 
299  if( _hasDerivatives )
300  {
301  glTexImage3D( GL_TEXTURE_3D,
302  0, GL_RGBA, _tW, _tH, _tD,
303  0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)(&data[0]) );
304  }else
305  {
306  glTexImage3D( GL_TEXTURE_3D,
307  0, GL_ALPHA, _tW, _tH, _tD,
308  0, GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid*)(&data[0]) );
309  }
310 
311  return true;
312 }
313 
314 
319 static void normalizeScaling
320 (
321  const uint32_t w,
322  const uint32_t h,
323  const uint32_t d,
324  VolumeScaling& scaling
325 )
326 {
327 //Correct proportions according to real size of volume
328  float maxS = float( LB_MAX( w, LB_MAX( h, d ) ));
329 
330  scaling.W *= w / maxS;
331  scaling.H *= h / maxS;
332  scaling.D *= d / maxS;
333 
334 //Make maximum proportion equal to 1.0
335  maxS = LB_MAX( scaling.W, LB_MAX( scaling.H, scaling.D ) );
336 
337  scaling.W /= maxS;
338  scaling.H /= maxS;
339  scaling.D /= maxS;
340 }
341 
342 
343 static bool readDimensionsAndScaling
344 (
345  FILE* file,
346  uint32_t& w, uint32_t& h, uint32_t& d,
347  VolumeScaling& volScaling
348 )
349 {
350  if( fscanf( file, "w=%u\n", &w ) == EOF )
351  return false;
352  if( fscanf( file, "h=%u\n", &h ) == EOF )
353  return false;
354  if( fscanf( file, "d=%u\n", &d ) != 1 )
355  {
356  LBERROR << "Can't read dimensions from header file" << std::endl;
357  return false;
358  }
359 
360  if( fscanf( file, "wScale=%g\n", &volScaling.W ) == EOF )
361  return false;
362  if( fscanf( file, "hScale=%g\n", &volScaling.H ) == EOF )
363  return false;
364  if( fscanf( file, "dScale=%g\n", &volScaling.D ) != 1 )
365  {
366  LBERROR << "Can't read scaling from header file: " << std::endl;
367  return false;
368  }
369 
370  if( w<1 || h<1 || d<1 ||
371  volScaling.W<0.001 ||
372  volScaling.H<0.001 ||
373  volScaling.W<0.001 )
374  {
375  LBERROR << "volume scaling is incorrect, check header file"<< std::endl;
376  return false;
377  }
378 
379  normalizeScaling( w, h, d, volScaling );
380 
381  LBLOG( eq::LOG_CUSTOM ) << " " << w << "x" << h << "x" << d << std::endl
382  << "( " << volScaling.W << " x "
383  << volScaling.H << " x "
384  << volScaling.D << " )" << std::endl;
385  return true;
386 }
387 
388 
389 static bool readTransferFunction( FILE* file, std::vector<uint8_t>& TF )
390 {
391  if( fscanf(file,"TF:\n") !=0 )
392  {
393  LBERROR << "Error in header file, can't find 'TF:' marker.";
394  return false;
395  }
396 
397  uint32_t TFSize;
398  if( fscanf( file, "size=%u\n", &TFSize ) == EOF )
399  return false;
400 
401  if( TFSize!=256 )
402  LBWARN << "Wrong size of transfer function, should be 256" << std::endl;
403 
404  TFSize = clip<int32_t>( TFSize, 1, 256 );
405  TF.resize( TFSize*4 );
406 
407  int tmp;
408  for( uint32_t i=0; i<TFSize; i++ )
409  {
410  if( fscanf( file, "r=%d\n", &tmp ) == EOF )
411  return false;
412  TF[4*i ] = tmp;
413  if( fscanf( file, "g=%d\n", &tmp ) == EOF )
414  return false;
415  TF[4*i+1] = tmp;
416  if( fscanf( file, "b=%d\n", &tmp ) == EOF )
417  return false;
418  TF[4*i+2] = tmp;
419  if( fscanf( file, "a=%d\n", &tmp ) != 1 )
420  {
421  LBERROR << "Failed to read entity #" << i
422  << " of TF from header file" << std::endl;
423  return i;
424  }
425  TF[4*i+3] = tmp;
426  }
427  return true;
428 }
429 
430 
431 static GLuint createPreintegrationTable( const uint8_t *Table )
432 {
433  LBLOG( eq::LOG_CUSTOM ) << "Calculating preintegration table" << std::endl;
434 
435  double rInt[256]; rInt[0] = 0.;
436  double gInt[256]; gInt[0] = 0.;
437  double bInt[256]; bInt[0] = 0.;
438  double aInt[256]; aInt[0] = 0.;
439 
440  // Creating SAT (Summed Area Tables) from averaged neighbouring RGBA values
441  for( int i=1; i<256; i++ )
442  {
443  // average Alpha from two neighbouring TF values
444  const double tauc = ( Table[(i-1)*4+3] + Table[i*4+3] ) / 2. / 255.;
445 
446  // SAT of average RGBs from two neighbouring TF values
447  // multiplied with Alpha
448  rInt[i] = rInt[i-1] + ( Table[(i-1)*4+0] + Table[i*4+0] )/2.*tauc;
449  gInt[i] = gInt[i-1] + ( Table[(i-1)*4+1] + Table[i*4+1] )/2.*tauc;
450  bInt[i] = bInt[i-1] + ( Table[(i-1)*4+2] + Table[i*4+2] )/2.*tauc;
451 
452  // SAT of average Alpha values
453  aInt[i] = aInt[i-1] + tauc;
454  }
455 
456  std::vector<GLubyte> lookupImg( 256*256*4, 255 ); // Preint Texture
457 
458  int lookupindex=0;
459 
460  for( int sb=0; sb<256; sb++ )
461  {
462  for( int sf=0; sf<256; sf++ )
463  {
464  int smin, smax;
465  if( sb<sf ) { smin=sb; smax=sf; }
466  else { smin=sf; smax=sb; }
467 
468  double rcol, gcol, bcol, acol;
469  if( smax != smin )
470  {
471  const double factor = 1. / (double)(smax-smin);
472  rcol = ( rInt[smax] - rInt[smin] ) * factor;
473  gcol = ( gInt[smax] - gInt[smin] ) * factor;
474  bcol = ( bInt[smax] - bInt[smin] ) * factor;
475  acol = exp( -( aInt[smax] - aInt[smin] ) * factor) * 255.;
476  } else
477  {
478  const int index = smin*4;
479  const double factor = 1./255.;
480  rcol = Table[index+0] * Table[index+3] * factor;
481  gcol = Table[index+1] * Table[index+3] * factor;
482  bcol = Table[index+2] * Table[index+3] * factor;
483  acol = exp( -Table[index+3] * factor ) * 256.;
484  }
485  lookupImg[lookupindex++] = clip( static_cast<int>(rcol), 0, 255 );
486  lookupImg[lookupindex++] = clip( static_cast<int>(gcol), 0, 255 );
487  lookupImg[lookupindex++] = clip( static_cast<int>(bcol), 0, 255 );
488  lookupImg[lookupindex++] = clip( static_cast<int>(acol), 0, 255 );
489  }
490  }
491 
492  GLuint preintName = 0;
493  glGenTextures( 1, &preintName );
494  glBindTexture( GL_TEXTURE_2D, preintName );
495  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
496  GL_RGBA, GL_UNSIGNED_BYTE, &lookupImg[0] );
497 
498  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
499  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
500  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
501  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
502 
503  return preintName;
504 }
505 
506 
507 }
float H
height scale
Definition: rawVolModel.h:66
float Db
Depth border (necessary for preintegration)
Definition: rawVolModel.h:58
float D
Depth of data in texture (0..1].
Definition: rawVolModel.h:56
User-defined log topics (65536)
Definition: client/log.h:35
float W
Width of data in texture (0..1].
Definition: rawVolModel.h:54
Structure that contain actual dimensions of data that is stored in volume texture.
Definition: rawVolModel.h:52
Just helping structure to automatically close files.
Definition: hlp.h:41
Contain overal volume proportions relatively [-1,-1,-1]..[1,1,1] cube.
Definition: rawVolModel.h:63
float D
depth scale
Definition: rawVolModel.h:67
float W
width scale
Definition: rawVolModel.h:65
float H
Height of data in texture (0..1].
Definition: rawVolModel.h:55
float Do
Depth offset (start of range)
Definition: rawVolModel.h:57