Blender V2.61 - r43446

WTURBULENCE.cpp

Go to the documentation of this file.
00001 
00004 
00005 // This file is part of Wavelet Turbulence.
00006 // 
00007 // Wavelet Turbulence is free software: you can redistribute it and/or modify
00008 // it under the terms of the GNU General Public License as published by
00009 // the Free Software Foundation, either version 3 of the License, or
00010 // (at your option) any later version.
00011 // 
00012 // Wavelet Turbulence is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 // 
00017 // You should have received a copy of the GNU General Public License
00018 // along with Wavelet Turbulence.  If not, see <http://www.gnu.org/licenses/>.
00019 // 
00020 // Copyright 2008 Theodore Kim and Nils Thuerey
00021 // 
00022 // WTURBULENCE handling
00024 // Parallelized turbulence even further. TNT matrix library functions
00025 // rewritten to improve performance.
00026 //      - MiikaH
00028 
00029 #include "WTURBULENCE.h"
00030 #include "INTERPOLATE.h"
00031 #include "IMAGE.h"
00032 #include <MERSENNETWISTER.h>
00033 #include "WAVELET_NOISE.h"
00034 #include "FFT_NOISE.h"
00035 #include "EIGENVALUE_HELPER.h"
00036 #include "LU_HELPER.h"
00037 #include "SPHERE.h"
00038 #include <zlib.h>
00039 #include <math.h>
00040 
00041 // needed to access static advection functions
00042 #include "FLUID_3D.h"
00043 
00044 #if PARALLEL==1
00045 #include <omp.h>
00046 #endif // PARALLEL 
00047 
00048 // 2^ {-5/6}
00049 static const float persistence = 0.56123f;
00050 
00052 // constructor
00054 WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype)
00055 {
00056     // if noise magnitude is below this threshold, its contribution
00057     // is negilgible, so stop evaluating new octaves
00058     _cullingThreshold = 1e-3;
00059     
00060     // factor by which to increase the simulation resolution
00061     _amplify = amplify;
00062     
00063     // manually adjust the overall amount of turbulence
00064     // DG - RNA-fied _strength = 2.;
00065     
00066     // add the corresponding octaves of noise
00067     _octaves = (int)(log((float)_amplify) / log(2.0f) + 0.5); // XXX DEBUG/ TODO: int casting correct? - dg
00068     
00069     // noise resolution
00070     _xResBig = _amplify * xResSm;
00071     _yResBig = _amplify * yResSm;
00072     _zResBig = _amplify * zResSm;
00073     _resBig = Vec3Int(_xResBig, _yResBig, _zResBig);
00074     _invResBig = Vec3(1./(float)_resBig[0], 1./(float)_resBig[1], 1./(float)_resBig[2]);
00075     _slabSizeBig = _xResBig*_yResBig;
00076     _totalCellsBig = _slabSizeBig * _zResBig;
00077     
00078     // original / small resolution
00079     _xResSm = xResSm;
00080     _yResSm = yResSm;
00081     _zResSm = zResSm;
00082     _resSm = Vec3Int(xResSm, yResSm, zResSm);
00083     _invResSm = Vec3(1./(float)_resSm[0], 1./(float)_resSm[1], 1./(float)_resSm[2] );
00084     _slabSizeSm = _xResSm*_yResSm;
00085     _totalCellsSm = _slabSizeSm * _zResSm;
00086     
00087     // allocate high resolution density field
00088     _totalStepsBig = 0;
00089     _densityBig = new float[_totalCellsBig];
00090     _densityBigOld = new float[_totalCellsBig]; 
00091     
00092     for(int i = 0; i < _totalCellsBig; i++) {
00093         _densityBig[i] = 
00094         _densityBigOld[i] = 0.;
00095     }
00096     
00097     // allocate & init texture coordinates
00098     _tcU = new float[_totalCellsSm];
00099     _tcV = new float[_totalCellsSm];
00100     _tcW = new float[_totalCellsSm];
00101     _tcTemp = new float[_totalCellsSm];
00102     
00103     // map all 
00104     const float dx = 1./(float)(_resSm[0]);
00105     const float dy = 1./(float)(_resSm[1]);
00106     const float dz = 1./(float)(_resSm[2]);
00107     int index = 0;
00108     for (int z = 0; z < _zResSm; z++) 
00109     for (int y = 0; y < _yResSm; y++) 
00110         for (int x = 0; x < _xResSm; x++, index++)
00111         {
00112         _tcU[index] = x*dx;
00113         _tcV[index] = y*dy;
00114         _tcW[index] = z*dz;
00115         _tcTemp[index] = 0.;
00116         }
00117     
00118     // noise tiles
00119     _noiseTile = new float[noiseTileSize * noiseTileSize * noiseTileSize];
00120     /*
00121     std::string noiseTileFilename = std::string("noise.wavelets");
00122     generateTile_WAVELET(_noiseTile, noiseTileFilename);
00123     */
00124     setNoise(noisetype);
00125     /*
00126     std::string noiseTileFilename = std::string("noise.fft");
00127     generatTile_FFT(_noiseTile, noiseTileFilename);
00128     */
00129 }
00130 
00132 // destructor
00134 WTURBULENCE::~WTURBULENCE() {
00135   delete[] _densityBig;
00136   delete[] _densityBigOld;
00137 
00138   delete[] _tcU;
00139   delete[] _tcV;
00140   delete[] _tcW;
00141   delete[] _tcTemp;
00142 
00143   delete[] _noiseTile;
00144 }
00145 
00147 // Change noise type
00148 //
00149 // type (1<<0) = wavelet / 2
00150 // type (1<<1) = FFT / 4
00151 // type (1<<2) = curl / 8
00153 void WTURBULENCE::setNoise(int type)
00154 {
00155     if(type == (1<<1)) // FFT
00156     {
00157         // needs fft
00158         #ifdef WITH_FFTW3
00159         std::string noiseTileFilename = std::string("noise.fft");
00160         generatTile_FFT(_noiseTile, noiseTileFilename);
00161         #endif
00162     }
00163     else if(type == (1<<2)) // curl
00164     {
00165         // TODO: not supported yet
00166     }
00167     else // standard - wavelet
00168     {
00169         std::string noiseTileFilename = std::string("noise.wavelets");
00170         generateTile_WAVELET(_noiseTile, noiseTileFilename);
00171     }
00172 }
00173 
00174 // init direct access functions from blender
00175 void WTURBULENCE::initBlenderRNA(float *strength)
00176 {
00177     _strength = strength;
00178 }
00179 
00181 // Get the smallest valid x derivative
00182 //
00183 // Takes the one-sided finite difference in both directions and
00184 // selects the smaller of the two
00186 static float minDx(int x, int y, int z, float* input, Vec3Int res)
00187 {
00188   const int index = x + y * res[0] + z * res[0] * res[1];
00189   const int maxx = res[0]-2;
00190 
00191   // get grid values
00192   float center = input[index];
00193   float left  = (x <= 1)    ? FLT_MAX : input[index - 1];
00194   float right = (x >= maxx) ? FLT_MAX : input[index + 1];
00195 
00196   const float dx = res[0];
00197 
00198   // get all the derivative estimates
00199   float dLeft   = (x <= 1)     ? FLT_MAX : (center - left) * dx;
00200   float dRight  = (x >= maxx)  ? FLT_MAX : (right - center) * dx;
00201   float dCenter = (x <= 1 || x >= maxx) ? FLT_MAX : (right - left) * dx * 0.5f;
00202 
00203   // if it's on a boundary, only one estimate is valid
00204   if (x <= 1) return dRight;
00205   if (x >= maxx) return dLeft;
00206 
00207   // if it's not on a boundary, get the smallest one
00208   float finalD;
00209   finalD = (fabs(dCenter) < fabs(dRight)) ? dCenter : dRight;
00210   finalD = (fabs(finalD)  < fabs(dLeft))  ? finalD  : dLeft;
00211 
00212   return finalD;
00213 }
00214 
00216 // get the smallest valid y derivative
00217 //
00218 // Takes the one-sided finite difference in both directions and
00219 // selects the smaller of the two
00221 static float minDy(int x, int y, int z, float* input, Vec3Int res)
00222 {
00223   const int index = x + y * res[0] + z * res[0] * res[1];
00224   const int maxy = res[1]-2;
00225 
00226   // get grid values
00227   float center = input[index];
00228   float down  = (y <= 1) ? FLT_MAX : input[index - res[0]];
00229   float up = (y >= maxy) ? FLT_MAX : input[index + res[0]];
00230 
00231   const float dx = res[1]; // only for square domains
00232 
00233   // get all the derivative estimates
00234   float dDown   = (y <= 1)  ? FLT_MAX : (center - down) * dx;
00235   float dUp  = (y >= maxy)  ? FLT_MAX : (up - center) * dx;
00236   float dCenter = (y <= 1 || y >= maxy) ? FLT_MAX : (up - down) * dx * 0.5f;
00237 
00238   // if it's on a boundary, only one estimate is valid
00239   if (y <= 1) return dUp;
00240   if (y >= maxy) return dDown;
00241 
00242   // if it's not on a boundary, get the smallest one
00243   float finalD = (fabs(dCenter) < fabs(dUp)) ? dCenter : dUp;
00244   finalD = (fabs(finalD) < fabs(dDown)) ? finalD : dDown;
00245 
00246   return finalD;
00247 }
00248 
00250 // get the smallest valid z derivative
00251 //
00252 // Takes the one-sided finite difference in both directions and
00253 // selects the smaller of the two
00255 static float minDz(int x, int y, int z, float* input, Vec3Int res)
00256 {
00257   const int slab = res[0]*res[1];
00258   const int index = x + y * res[0] + z * slab;
00259   const int maxz = res[2]-2;
00260 
00261   // get grid values
00262   float center = input[index];
00263   float front  = (z <= 1) ? FLT_MAX : input[index - slab];
00264   float back = (z >= maxz) ? FLT_MAX : input[index + slab];
00265 
00266   const float dx = res[2]; // only for square domains
00267 
00268   // get all the derivative estimates
00269   float dfront   = (z <= 1)  ? FLT_MAX : (center - front) * dx;
00270   float dback  = (z >= maxz)  ? FLT_MAX : (back - center) * dx;
00271   float dCenter = (z <= 1 || z >= maxz) ? FLT_MAX : (back - front) * dx * 0.5f;
00272 
00273   // if it's on a boundary, only one estimate is valid
00274   if (z <= 1) return dback;
00275   if (z >= maxz) return dfront;
00276 
00277   // if it's not on a boundary, get the smallest one
00278   float finalD = (fabs(dCenter) < fabs(dback)) ? dCenter : dback;
00279   finalD = (fabs(finalD) < fabs(dfront)) ? finalD : dfront;
00280 
00281   return finalD;
00282 }
00283 
00285 // handle texture coordinates (advection, reset, eigenvalues), 
00286 // Beware -- uses big density maccormack as temporary arrays
00288 void WTURBULENCE::advectTextureCoordinates (float dtOrg, float* xvel, float* yvel, float* zvel, float *tempBig1, float *tempBig2) {
00289 
00290   // advection
00291   SWAP_POINTERS(_tcTemp, _tcU);
00292   FLUID_3D::copyBorderX(_tcTemp, _resSm, 0 , _resSm[2]);
00293   FLUID_3D::copyBorderY(_tcTemp, _resSm, 0 , _resSm[2]);
00294   FLUID_3D::copyBorderZ(_tcTemp, _resSm, 0 , _resSm[2]);
00295   FLUID_3D::advectFieldMacCormack1(dtOrg, xvel, yvel, zvel, 
00296       _tcTemp, tempBig1, _resSm, 0 , _resSm[2]);
00297   FLUID_3D::advectFieldMacCormack2(dtOrg, xvel, yvel, zvel, 
00298       _tcTemp, _tcU, tempBig1, tempBig2, _resSm, NULL, 0 , _resSm[2]);
00299 
00300   SWAP_POINTERS(_tcTemp, _tcV);
00301   FLUID_3D::copyBorderX(_tcTemp, _resSm, 0 , _resSm[2]);
00302   FLUID_3D::copyBorderY(_tcTemp, _resSm, 0 , _resSm[2]);
00303   FLUID_3D::copyBorderZ(_tcTemp, _resSm, 0 , _resSm[2]);
00304   FLUID_3D::advectFieldMacCormack1(dtOrg, xvel, yvel, zvel, 
00305       _tcTemp, tempBig1, _resSm, 0 , _resSm[2]);
00306   FLUID_3D::advectFieldMacCormack2(dtOrg, xvel, yvel, zvel, 
00307       _tcTemp, _tcV, tempBig1, tempBig2, _resSm, NULL, 0 , _resSm[2]);
00308 
00309   SWAP_POINTERS(_tcTemp, _tcW);
00310   FLUID_3D::copyBorderX(_tcTemp, _resSm, 0 , _resSm[2]);
00311   FLUID_3D::copyBorderY(_tcTemp, _resSm, 0 , _resSm[2]);
00312   FLUID_3D::copyBorderZ(_tcTemp, _resSm, 0 , _resSm[2]);
00313   FLUID_3D::advectFieldMacCormack1(dtOrg, xvel, yvel, zvel, 
00314       _tcTemp, tempBig1, _resSm, 0 , _resSm[2]);
00315   FLUID_3D::advectFieldMacCormack2(dtOrg, xvel, yvel, zvel, 
00316       _tcTemp, _tcW, tempBig1, tempBig2, _resSm, NULL, 0 , _resSm[2]);
00317 }
00318 
00320 // Compute the eigenvalues of the advected texture
00322 void WTURBULENCE::computeEigenvalues(float *_eigMin, float *_eigMax) {
00323   // stats
00324   float maxeig = -1.;
00325   float mineig = 10.;
00326 
00327   // texture coordinate eigenvalues
00328   for (int z = 1; z < _zResSm-1; z++) {
00329     for (int y = 1; y < _yResSm-1; y++) 
00330       for (int x = 1; x < _xResSm-1; x++)
00331       {
00332         const int index = x+ y *_resSm[0] + z*_slabSizeSm;
00333 
00334         // compute jacobian
00335         float jacobian[3][3] = {
00336           { minDx(x, y, z, _tcU, _resSm), minDx(x, y, z, _tcV, _resSm), minDx(x, y, z, _tcW, _resSm) } ,
00337           { minDy(x, y, z, _tcU, _resSm), minDy(x, y, z, _tcV, _resSm), minDy(x, y, z, _tcW, _resSm) } ,
00338           { minDz(x, y, z, _tcU, _resSm), minDz(x, y, z, _tcV, _resSm), minDz(x, y, z, _tcW, _resSm) }
00339         };
00340 
00341         // ONLY compute the eigenvalues after checking that the matrix
00342         // is nonsingular
00343         sLU LU = computeLU(jacobian);
00344 
00345         if (isNonsingular(LU))
00346         {
00347           // get the analytic eigenvalues, quite slow right now...
00348           Vec3 eigenvalues = Vec3(1.);
00349           computeEigenvalues3x3( &eigenvalues[0], jacobian);
00350           _eigMax[index] = MAX3V(eigenvalues);
00351           _eigMin[index] = MIN3V(eigenvalues);
00352           maxeig = MAX(_eigMax[index],maxeig);
00353           mineig = MIN(_eigMin[index],mineig);
00354         }
00355         else
00356         {
00357           _eigMax[index] = 10.0f;
00358           _eigMin[index] = 0.1;
00359         }
00360       }
00361   }
00362 }
00363 
00365 // advect & reset texture coordinates based on eigenvalues
00367 void WTURBULENCE::resetTextureCoordinates(float *_eigMin, float *_eigMax) 
00368 {
00369   // allowed deformation of the textures
00370   const float limit = 2.f;
00371   const float limitInv = 1./limit;
00372 
00373   // standard reset
00374   int resets = 0;
00375   const float dx = 1./(float)(_resSm[0]);
00376   const float dy = 1./(float)(_resSm[1]);
00377   const float dz = 1./(float)(_resSm[2]);
00378 
00379   for (int z = 1; z < _zResSm-1; z++)
00380     for (int y = 1; y < _yResSm-1; y++)
00381       for (int x = 1; x < _xResSm-1; x++)
00382       {
00383         const int index = x+ y *_resSm[0] + z*_slabSizeSm;
00384         if (_eigMax[index] > limit || _eigMin[index] < limitInv)
00385         {
00386           _tcU[index] = (float)x * dx;
00387           _tcV[index] = (float)y * dy;
00388           _tcW[index] = (float)z * dz;
00389           resets++;
00390         }
00391       }
00392 }
00393 
00395 // Compute the highest frequency component of the wavelet
00396 // decomposition
00398 void WTURBULENCE::decomposeEnergy(float *_energy, float *_highFreqEnergy)
00399 {
00400   // do the decomposition -- the goal here is to have
00401   // the energy with the high frequency component stomped out
00402   // stored in _tcTemp when it is done. _highFreqEnergy is only used
00403   // as an additional temp array
00404   
00405   // downsample input
00406   downsampleXNeumann(_highFreqEnergy, _energy, _xResSm, _yResSm, _zResSm);
00407   downsampleYNeumann(_tcTemp, _highFreqEnergy, _xResSm, _yResSm, _zResSm);
00408   downsampleZNeumann(_highFreqEnergy, _tcTemp, _xResSm, _yResSm, _zResSm);
00409 
00410   // upsample input
00411   upsampleZNeumann(_tcTemp, _highFreqEnergy, _xResSm, _yResSm, _zResSm);
00412   upsampleYNeumann(_highFreqEnergy, _tcTemp, _xResSm, _yResSm, _zResSm);
00413   upsampleXNeumann(_tcTemp, _highFreqEnergy, _xResSm, _yResSm, _zResSm);
00414 
00415   // subtract the down and upsampled field from the original field -- 
00416   // what should be left over is solely the high frequency component
00417     int index = 0;
00418   for (int z = 0; z < _zResSm; z++) 
00419     for (int y = 0; y < _yResSm; y++) {
00420       for (int x = 0; x < _xResSm; x++, index++) {
00421         // brute force reset of boundaries
00422         if(z >= _zResSm - 1 || x >= _xResSm - 1 || y >= _yResSm - 1 || z <= 0 || y <= 0 || x <= 0) 
00423           _highFreqEnergy[index] = 0.; 
00424         else 
00425           _highFreqEnergy[index] = _energy[index] - _tcTemp[index];
00426     }
00427   }
00428 }
00429 
00431 // compute velocity from energies and march into obstacles
00432 // for wavelet decomposition
00434 void WTURBULENCE::computeEnergy(float *_energy, float* xvel, float* yvel, float* zvel, unsigned char *obstacles) 
00435 {
00436   // compute everywhere
00437   for (int x = 0; x < _totalCellsSm; x++) 
00438     _energy[x] = 0.5f * (xvel[x] * xvel[x] + yvel[x] * yvel[x] + zvel[x] * zvel[x]);
00439 
00440   FLUID_3D::copyBorderX(_energy, _resSm, 0 , _resSm[2]);
00441   FLUID_3D::copyBorderY(_energy, _resSm, 0 , _resSm[2]);
00442   FLUID_3D::copyBorderZ(_energy, _resSm, 0 , _resSm[2]);
00443 
00444   // pseudo-march the values into the obstacles
00445   // the wavelet upsampler only uses a 3x3 support neighborhood, so
00446   // propagating the values in by 4 should be sufficient
00447   int index;
00448 
00449   // iterate
00450   for (int iter = 0; iter < 4; iter++)
00451   {
00452     index = _slabSizeSm + _xResSm + 1;
00453     for (int z = 1; z < _zResSm - 1; z++, index += 2 * _xResSm)
00454       for (int y = 1; y < _yResSm - 1; y++, index += 2)
00455         for (int x = 1; x < _xResSm - 1; x++, index++)
00456           if (obstacles[index] && obstacles[index] != RETIRED)
00457           {
00458             float sum = 0.0f;
00459             int valid = 0;
00460 
00461             if (!obstacles[index + 1] || obstacles[index + 1] == RETIRED)
00462             {
00463               sum += _energy[index + 1];
00464               valid++;
00465             }
00466             if (!obstacles[index - 1] || obstacles[index - 1] == RETIRED)
00467             {
00468               sum += _energy[index - 1];
00469               valid++;
00470             }
00471             if (!obstacles[index + _xResSm] || obstacles[index + _xResSm] == RETIRED)
00472             {
00473               sum += _energy[index + _xResSm];
00474               valid++;
00475             }
00476             if (!obstacles[index - _xResSm] || obstacles[index - _xResSm] == RETIRED)
00477             {
00478               sum += _energy[index - _xResSm];
00479               valid++;
00480             }
00481             if (!obstacles[index + _slabSizeSm] || obstacles[index + _slabSizeSm] == RETIRED)
00482             {
00483               sum += _energy[index + _slabSizeSm];
00484               valid++;
00485             }
00486             if (!obstacles[index - _slabSizeSm] || obstacles[index - _slabSizeSm] == RETIRED)
00487             {
00488               sum += _energy[index - _slabSizeSm];
00489               valid++;
00490             }
00491             if (valid > 0)
00492             {
00493               _energy[index] = sum / (float)valid;
00494               obstacles[index] = MARCHED;
00495             }
00496           }
00497     index = _slabSizeSm + _xResSm + 1;
00498     for (int z = 1; z < _zResSm - 1; z++, index += 2 * _xResSm)
00499       for (int y = 1; y < _yResSm - 1; y++, index += 2)
00500         for (int x = 1; x < _xResSm - 1; x++, index++)
00501           if (obstacles[index] == MARCHED)
00502             obstacles[index] = RETIRED;
00503   }
00504   index = _slabSizeSm + _xResSm + 1;
00505   for (int z = 1; z < _zResSm - 1; z++, index += 2 * _xResSm)
00506     for (int y = 1; y < _yResSm - 1; y++, index += 2)
00507       for (int x = 1; x < _xResSm - 1; x++, index++)
00508         if (obstacles[index])
00509           obstacles[index] = 1;
00510 }
00511 
00513 // Evaluate derivatives
00515 Vec3 WTURBULENCE::WVelocity(Vec3 orgPos)
00516 {
00517   // arbitrarily offset evaluation points
00518   const Vec3 p1 = orgPos + Vec3(NOISE_TILE_SIZE/2.0,0,0);
00519   const Vec3 p2 = orgPos + Vec3(0,NOISE_TILE_SIZE/2.0,0);
00520   const Vec3 p3 = orgPos + Vec3(0,0,NOISE_TILE_SIZE/2.0);
00521 
00522   const float f1y = WNoiseDy(p1, _noiseTile);
00523   const float f1z = WNoiseDz(p1, _noiseTile);
00524 
00525   const float f2x = WNoiseDx(p2, _noiseTile);
00526   const float f2z = WNoiseDz(p2, _noiseTile);
00527 
00528   const float f3x = WNoiseDx(p3, _noiseTile);
00529   const float f3y = WNoiseDy(p3, _noiseTile);
00530 
00531   Vec3 ret = Vec3( 
00532       f3y - f2z,
00533       f1z - f3x,
00534       f2x - f1y ); 
00535   return ret;
00536 }
00537 
00539 // Evaluate derivatives with Jacobian
00541 Vec3 WTURBULENCE::WVelocityWithJacobian(Vec3 orgPos, float* xUnwarped, float* yUnwarped, float* zUnwarped)
00542 {
00543   // arbitrarily offset evaluation points
00544   const Vec3 p1 = orgPos + Vec3(NOISE_TILE_SIZE/2.0,0,0);
00545   const Vec3 p2 = orgPos + Vec3(0,NOISE_TILE_SIZE/2.0,0);
00546   const Vec3 p3 = orgPos + Vec3(0,0,NOISE_TILE_SIZE/2.0);
00547 
00548   Vec3 final;
00549   final[0] = WNoiseDx(p1, _noiseTile);
00550   final[1] = WNoiseDy(p1, _noiseTile);
00551   final[2] = WNoiseDz(p1, _noiseTile);
00552   // UNUSED const float f1x = xUnwarped[0] * final[0] + xUnwarped[1] * final[1] + xUnwarped[2] * final[2];
00553   const float f1y = yUnwarped[0] * final[0] + yUnwarped[1] * final[1] + yUnwarped[2] * final[2];
00554   const float f1z = zUnwarped[0] * final[0] + zUnwarped[1] * final[1] + zUnwarped[2] * final[2];
00555 
00556   final[0] = WNoiseDx(p2, _noiseTile);
00557   final[1] = WNoiseDy(p2, _noiseTile);
00558   final[2] = WNoiseDz(p2, _noiseTile);
00559   const float f2x = xUnwarped[0] * final[0] + xUnwarped[1] * final[1] + xUnwarped[2] * final[2];
00560   // UNUSED const float f2y = yUnwarped[0] * final[0] + yUnwarped[1] * final[1] + yUnwarped[2] * final[2];
00561   const float f2z = zUnwarped[0] * final[0] + zUnwarped[1] * final[1] + zUnwarped[2] * final[2];
00562 
00563   final[0] = WNoiseDx(p3, _noiseTile);
00564   final[1] = WNoiseDy(p3, _noiseTile);
00565   final[2] = WNoiseDz(p3, _noiseTile);
00566   const float f3x = xUnwarped[0] * final[0] + xUnwarped[1] * final[1] + xUnwarped[2] * final[2];
00567   const float f3y = yUnwarped[0] * final[0] + yUnwarped[1] * final[1] + yUnwarped[2] * final[2];
00568   // UNUSED const float f3z = zUnwarped[0] * final[0] + zUnwarped[1] * final[1] + zUnwarped[2] * final[2];
00569 
00570   Vec3 ret = Vec3( 
00571       f3y - f2z,
00572       f1z - f3x,
00573       f2x - f1y ); 
00574   return ret;
00575 }
00576 
00577 
00579 // perform an actual noise advection step
00581 /*void WTURBULENCE::stepTurbulenceReadable(float dtOrg, float* xvel, float* yvel, float* zvel, unsigned char *obstacles) 
00582 {
00583     // enlarge timestep to match grid
00584     const float dt = dtOrg * _amplify;
00585     const float invAmp = 1.0f / _amplify;
00586     float *tempBig1 = new float[_totalCellsBig];
00587     float *tempBig2 = new float[_totalCellsBig];
00588     float *bigUx = new float[_totalCellsBig];
00589     float *bigUy = new float[_totalCellsBig];
00590     float *bigUz = new float[_totalCellsBig]; 
00591     float *_energy = new float[_totalCellsSm];
00592     float *highFreqEnergy = new float[_totalCellsSm];
00593     float *eigMin  = new float[_totalCellsSm];
00594     float *eigMax  = new float[_totalCellsSm];
00595 
00596     memset(tempBig1, 0, sizeof(float)*_totalCellsBig);
00597     memset(tempBig2, 0, sizeof(float)*_totalCellsBig);
00598     memset(highFreqEnergy, 0, sizeof(float)*_totalCellsSm);
00599     memset(eigMin, 0, sizeof(float)*_totalCellsSm);
00600     memset(eigMax, 0, sizeof(float)*_totalCellsSm);
00601 
00602     // prepare textures
00603     advectTextureCoordinates(dtOrg, xvel,yvel,zvel, tempBig1, tempBig2);
00604 
00605   // compute eigenvalues of the texture coordinates
00606   computeEigenvalues(eigMin, eigMax);
00607 
00608   // do wavelet decomposition of energy
00609   computeEnergy(_energy, xvel, yvel, zvel, obstacles);
00610   decomposeEnergy(_energy, highFreqEnergy);
00611 
00612   // zero out coefficients inside of the obstacle
00613   for (int x = 0; x < _totalCellsSm; x++)
00614     if (obstacles[x]) _energy[x] = 0.f;
00615 
00616   float maxVelocity = 0.;
00617   for (int z = 1; z < _zResBig - 1; z++) 
00618     for (int y = 1; y < _yResBig - 1; y++) 
00619       for (int x = 1; x < _xResBig - 1; x++)
00620       {
00621         // get unit position for both fine and coarse grid
00622         const Vec3 pos = Vec3(x,y,z);
00623         const Vec3 posSm = pos * invAmp;
00624         
00625         // get grid index for both fine and coarse grid
00626         const int index = x + y *_xResBig + z *_slabSizeBig;
00627         const int indexSmall = (int)posSm[0] + (int)posSm[1] * _xResSm + (int)posSm[2] * _slabSizeSm;
00628         
00629         // get a linearly interpolated velocity and texcoords
00630         // from the coarse grid
00631         Vec3 vel = INTERPOLATE::lerp3dVec( xvel,yvel,zvel, 
00632             posSm[0], posSm[1], posSm[2], _xResSm,_yResSm,_zResSm);
00633         Vec3 uvw = INTERPOLATE::lerp3dVec( _tcU,_tcV,_tcW, 
00634             posSm[0], posSm[1], posSm[2], _xResSm,_yResSm,_zResSm);
00635 
00636         // multiply the texture coordinate by _resSm so that turbulence
00637         // synthesis begins at the first octave that the coarse grid 
00638         // cannot capture
00639         Vec3 texCoord = Vec3(uvw[0] * _resSm[0], 
00640                              uvw[1] * _resSm[1],
00641                              uvw[2] * _resSm[2]); 
00642 
00643         // retrieve wavelet energy at highest frequency
00644         float energy = INTERPOLATE::lerp3d(
00645             highFreqEnergy, posSm[0],posSm[1],posSm[2], _xResSm, _yResSm, _zResSm);
00646 
00647         // base amplitude for octave 0
00648         float coefficient = sqrtf(2.0f * fabs(energy));
00649         const float amplitude = *_strength * fabs(0.5 * coefficient) * persistence;
00650 
00651         // add noise to velocity, but only if the turbulence is
00652         // sufficiently undeformed, and the energy is large enough
00653         // to make a difference
00654         const bool addNoise = eigMax[indexSmall] < 2. && 
00655                               eigMin[indexSmall] > 0.5;
00656         if (addNoise && amplitude > _cullingThreshold) {
00657           // base amplitude for octave 0
00658           float amplitudeScaled = amplitude;
00659 
00660           for (int octave = 0; octave < _octaves; octave++)
00661           {
00662             // multiply the vector noise times the maximum allowed
00663             // noise amplitude at this octave, and add it to the total
00664             vel += WVelocity(texCoord) * amplitudeScaled;
00665 
00666             // scale coefficient for next octave
00667             amplitudeScaled *= persistence;
00668             texCoord *= 2.0f;
00669           }
00670         }
00671 
00672         // Store velocity + turbulence in big grid for maccormack step
00673         //
00674         // If you wanted to save memory, you would instead perform a 
00675         // semi-Lagrangian backtrace for the current grid cell here. Then
00676         // you could just throw the velocity away.
00677         bigUx[index] = vel[0];
00678         bigUy[index] = vel[1];
00679         bigUz[index] = vel[2];
00680 
00681         // compute the velocity magnitude for substepping later
00682         const float velMag = bigUx[index] * bigUx[index] + 
00683                              bigUy[index] * bigUy[index] + 
00684                              bigUz[index] * bigUz[index];
00685         if (velMag > maxVelocity) maxVelocity = velMag;
00686 
00687         // zero out velocity inside obstacles
00688         float obsCheck = INTERPOLATE::lerp3dToFloat(
00689             obstacles, posSm[0], posSm[1], posSm[2], _xResSm, _yResSm, _zResSm); 
00690         if (obsCheck > 0.95) 
00691           bigUx[index] = bigUy[index] = bigUz[index] = 0.;
00692       }
00693 
00694   // prepare density for an advection
00695   SWAP_POINTERS(_densityBig, _densityBigOld);
00696 
00697   // based on the maximum velocity present, see if we need to substep,
00698   // but cap the maximum number of substeps to 5
00699   const int maxSubSteps = 5; 
00700   maxVelocity = sqrt(maxVelocity) * dt;
00701   int totalSubsteps = (int)(maxVelocity / (float)maxSubSteps);
00702   totalSubsteps = (totalSubsteps < 1) ? 1 : totalSubsteps;
00703   totalSubsteps = (totalSubsteps > maxSubSteps) ? maxSubSteps : totalSubsteps;
00704   const float dtSubdiv = dt / (float)totalSubsteps;
00705 
00706   // set boundaries of big velocity grid
00707   FLUID_3D::setZeroX(bigUx, _resBig, 0, _resBig[2]); 
00708   FLUID_3D::setZeroY(bigUy, _resBig, 0, _resBig[2]); 
00709   FLUID_3D::setZeroZ(bigUz, _resBig, 0, _resBig[2]);
00710 
00711   // do the MacCormack advection, with substepping if necessary
00712   for(int substep = 0; substep < totalSubsteps; substep++)
00713   {
00714     FLUID_3D::advectFieldMacCormack(dtSubdiv, bigUx, bigUy, bigUz, 
00715         _densityBigOld, _densityBig, tempBig1, tempBig2, _resBig, NULL);
00716 
00717     if (substep < totalSubsteps - 1) 
00718       SWAP_POINTERS(_densityBig, _densityBigOld);
00719   } // substep
00720   
00721   // wipe the density borders
00722   FLUID_3D::setZeroBorder(_densityBig, _resBig, 0, _resBig[2]);
00723     
00724   // reset texture coordinates now in preparation for next timestep
00725   // Shouldn't do this before generating the noise because then the 
00726   // eigenvalues stored do not reflect the underlying texture coordinates
00727   resetTextureCoordinates(eigMin, eigMax);
00728   
00729   delete[] tempBig1;
00730   delete[] tempBig2;
00731   delete[] bigUx;
00732   delete[] bigUy;
00733   delete[] bigUz;
00734   delete[] _energy;
00735   delete[] highFreqEnergy;
00736 
00737   delete[] eigMin;
00738   delete[] eigMax;
00739   
00740 
00741   _totalStepsBig++;
00742 }*/
00743 
00744 //struct
00745 
00747 // perform the full turbulence algorithm, including OpenMP 
00748 // if available
00750 void WTURBULENCE::stepTurbulenceFull(float dtOrg, float* xvel, float* yvel, float* zvel, unsigned char *obstacles)
00751 {
00752     // enlarge timestep to match grid
00753     const float dt = dtOrg * _amplify;
00754     const float invAmp = 1.0f / _amplify;
00755     float *tempBig1 = (float *)calloc(_totalCellsBig, sizeof(float));
00756     float *tempBig2 = (float *)calloc(_totalCellsBig, sizeof(float));
00757     float *bigUx = (float *)calloc(_totalCellsBig, sizeof(float));
00758     float *bigUy = (float *)calloc(_totalCellsBig, sizeof(float));
00759     float *bigUz = (float *)calloc(_totalCellsBig, sizeof(float)); 
00760     float *_energy = (float *)calloc(_totalCellsSm, sizeof(float));
00761     float *highFreqEnergy = (float *)calloc(_totalCellsSm, sizeof(float));
00762     float *eigMin  = (float *)calloc(_totalCellsSm, sizeof(float));
00763     float *eigMax  = (float *)calloc(_totalCellsSm, sizeof(float));
00764 
00765     memset(_tcTemp, 0, sizeof(float)*_totalCellsSm);
00766 
00767 
00768     // prepare textures
00769     advectTextureCoordinates(dtOrg, xvel,yvel,zvel, tempBig1, tempBig2);
00770 
00771     // do wavelet decomposition of energy
00772     computeEnergy(_energy, xvel, yvel, zvel, obstacles);
00773 
00774     for (int x = 0; x < _totalCellsSm; x++)
00775         if (obstacles[x]) _energy[x] = 0.f;
00776 
00777     decomposeEnergy(_energy, highFreqEnergy);
00778 
00779     // zero out coefficients inside of the obstacle
00780     for (int x = 0; x < _totalCellsSm; x++)
00781         if (obstacles[x]) highFreqEnergy[x] = 0.f;
00782 
00783     Vec3Int ressm(_xResSm, _yResSm, _zResSm);
00784     FLUID_3D::setNeumannX(highFreqEnergy, ressm, 0 , ressm[2]);
00785     FLUID_3D::setNeumannY(highFreqEnergy, ressm, 0 , ressm[2]);
00786     FLUID_3D::setNeumannZ(highFreqEnergy, ressm, 0 , ressm[2]);
00787 
00788 
00789    int threadval = 1;
00790 #if PARALLEL==1
00791   threadval = omp_get_max_threads();
00792 #endif
00793 
00794 
00795   // parallel region setup
00796   // Uses omp_get_max_trheads to get number of required cells.
00797   float* maxVelMagThreads = new float[threadval];
00798 
00799   for (int i=0; i<threadval; i++) maxVelMagThreads[i] = -1.0f;
00800 
00801 #if PARALLEL==1
00802 
00803 #pragma omp parallel
00804 #endif
00805   { float maxVelMag1 = 0.;
00806 #if PARALLEL==1
00807     const int id  = omp_get_thread_num(); /*, num = omp_get_num_threads(); */
00808 #endif
00809 
00810   // vector noise main loop
00811 #if PARALLEL==1
00812 #pragma omp for schedule(static,1)
00813 #endif
00814   for (int zSmall = 0; zSmall < _zResSm; zSmall++)
00815   {
00816   for (int ySmall = 0; ySmall < _yResSm; ySmall++) 
00817   for (int xSmall = 0; xSmall < _xResSm; xSmall++)
00818   {
00819     const int indexSmall = xSmall + ySmall * _xResSm + zSmall * _slabSizeSm;
00820 
00821     // compute jacobian
00822     float jacobian[3][3] = {
00823       { minDx(xSmall, ySmall, zSmall, _tcU, _resSm), minDx(xSmall, ySmall, zSmall, _tcV, _resSm), minDx(xSmall, ySmall, zSmall, _tcW, _resSm) } ,
00824       { minDy(xSmall, ySmall, zSmall, _tcU, _resSm), minDy(xSmall, ySmall, zSmall, _tcV, _resSm), minDy(xSmall, ySmall, zSmall, _tcW, _resSm) } ,
00825       { minDz(xSmall, ySmall, zSmall, _tcU, _resSm), minDz(xSmall, ySmall, zSmall, _tcV, _resSm), minDz(xSmall, ySmall, zSmall, _tcW, _resSm) }
00826     };
00827 
00828     // get LU factorization of texture jacobian and apply 
00829     // it to unit vectors
00830     sLU LU = computeLU(jacobian);
00831     float xUnwarped[3], yUnwarped[3], zUnwarped[3];
00832     float xWarped[3], yWarped[3], zWarped[3];
00833     bool nonSingular = isNonsingular(LU);
00834 
00835     xUnwarped[0] = 1.0f; xUnwarped[1] = 0.0f; xUnwarped[2] = 0.0f;
00836     yUnwarped[0] = 0.0f; yUnwarped[1] = 1.0f; yUnwarped[2] = 0.0f;
00837     zUnwarped[0] = 0.0f; zUnwarped[1] = 0.0f; zUnwarped[2] = 1.0f;
00838 
00839     xWarped[0] = 1.0f; xWarped[1] = 0.0f; xWarped[2] = 0.0f;
00840     yWarped[0] = 0.0f; yWarped[1] = 1.0f; yWarped[2] = 0.0f;
00841     zWarped[0] = 0.0f; zWarped[1] = 0.0f; zWarped[2] = 1.0f;
00842 
00843 #if 0
00844     // UNUSED
00845     float eigMax = 10.0f;
00846     float eigMin = 0.1f;
00847 #endif
00848     if (nonSingular)
00849     {
00850       solveLU3x3(LU, xUnwarped, xWarped);
00851       solveLU3x3(LU, yUnwarped, yWarped);
00852       solveLU3x3(LU, zUnwarped, zWarped);
00853 
00854       // compute the eigenvalues while we have the Jacobian available
00855       Vec3 eigenvalues = Vec3(1.);
00856       computeEigenvalues3x3( &eigenvalues[0], jacobian);
00857       eigMax[indexSmall] = MAX3V(eigenvalues);
00858       eigMin[indexSmall] = MIN3V(eigenvalues);
00859     }
00860     
00861     // make sure to skip one on the beginning and end
00862     int xStart = (xSmall == 0) ? 1 : 0;
00863     int xEnd   = (xSmall == _xResSm - 1) ? _amplify - 1 : _amplify;
00864     int yStart = (ySmall == 0) ? 1 : 0;
00865     int yEnd   = (ySmall == _yResSm - 1) ? _amplify - 1 : _amplify;
00866     int zStart = (zSmall == 0) ? 1 : 0;
00867     int zEnd   = (zSmall == _zResSm - 1) ? _amplify - 1 : _amplify;
00868       
00869     for (int zBig = zStart; zBig < zEnd; zBig++) 
00870     for (int yBig = yStart; yBig < yEnd; yBig++) 
00871     for (int xBig = xStart; xBig < xEnd; xBig++)
00872     {
00873       const int x = xSmall * _amplify + xBig;
00874       const int y = ySmall * _amplify + yBig;
00875       const int z = zSmall * _amplify + zBig;
00876       
00877       // get unit position for both fine and coarse grid
00878       const Vec3 pos = Vec3(x,y,z);
00879       const Vec3 posSm = pos * invAmp;
00880       
00881       // get grid index for both fine and coarse grid
00882       const int index = x + y *_xResBig + z *_slabSizeBig;
00883       
00884       // get a linearly interpolated velocity and texcoords
00885       // from the coarse grid
00886       Vec3 vel = INTERPOLATE::lerp3dVec( xvel,yvel,zvel, 
00887           posSm[0], posSm[1], posSm[2], _xResSm,_yResSm,_zResSm);
00888       Vec3 uvw = INTERPOLATE::lerp3dVec( _tcU,_tcV,_tcW, 
00889           posSm[0], posSm[1], posSm[2], _xResSm,_yResSm,_zResSm);
00890 
00891       // multiply the texture coordinate by _resSm so that turbulence
00892       // synthesis begins at the first octave that the coarse grid 
00893       // cannot capture
00894       Vec3 texCoord = Vec3(uvw[0] * _resSm[0], 
00895                            uvw[1] * _resSm[1],
00896                            uvw[2] * _resSm[2]); 
00897 
00898       // retrieve wavelet energy at highest frequency
00899       float energy = INTERPOLATE::lerp3d(
00900           highFreqEnergy, posSm[0],posSm[1],posSm[2], _xResSm, _yResSm, _zResSm);
00901 
00902       // base amplitude for octave 0
00903       float coefficient = sqrtf(2.0f * fabs(energy));
00904       const float amplitude = *_strength * fabs(0.5 * coefficient) * persistence;
00905 
00906       // add noise to velocity, but only if the turbulence is
00907       // sufficiently undeformed, and the energy is large enough
00908       // to make a difference
00909       const bool addNoise = eigMax[indexSmall] < 2. && 
00910                             eigMin[indexSmall] > 0.5;
00911       if (addNoise && amplitude > _cullingThreshold) {
00912         // base amplitude for octave 0
00913         float amplitudeScaled = amplitude;
00914 
00915         for (int octave = 0; octave < _octaves; octave++)
00916         {
00917           // multiply the vector noise times the maximum allowed
00918           // noise amplitude at this octave, and add it to the total
00919           vel += WVelocityWithJacobian(texCoord, &xUnwarped[0], &yUnwarped[0], &zUnwarped[0]) * amplitudeScaled;
00920 
00921           // scale coefficient for next octave
00922           amplitudeScaled *= persistence;
00923           texCoord *= 2.0f;
00924         }
00925       }
00926 
00927       // Store velocity + turbulence in big grid for maccormack step
00928       //
00929       // If you wanted to save memory, you would instead perform a 
00930       // semi-Lagrangian backtrace for the current grid cell here. Then
00931       // you could just throw the velocity away.
00932       bigUx[index] = vel[0];
00933       bigUy[index] = vel[1];
00934       bigUz[index] = vel[2];
00935 
00936       // compute the velocity magnitude for substepping later
00937       const float velMag = bigUx[index] * bigUx[index] + 
00938                            bigUy[index] * bigUy[index] + 
00939                            bigUz[index] * bigUz[index];
00940       if (velMag > maxVelMag1) maxVelMag1 = velMag;
00941 
00942       // zero out velocity inside obstacles
00943       float obsCheck = INTERPOLATE::lerp3dToFloat(
00944           obstacles, posSm[0], posSm[1], posSm[2], _xResSm, _yResSm, _zResSm); 
00945       if (obsCheck > 0.95) 
00946         bigUx[index] = bigUy[index] = bigUz[index] = 0.;
00947     } // xyz*/
00948 
00949 #if PARALLEL==1
00950     maxVelMagThreads[id] = maxVelMag1;
00951 #else
00952     maxVelMagThreads[0] = maxVelMag1;
00953 #endif
00954   }
00955   }
00956   } // omp
00957   
00958   // compute maximum over threads
00959   float maxVelMag = maxVelMagThreads[0];
00960 #if PARALLEL==1
00961   for (int i = 1; i < threadval; i++) 
00962     if (maxVelMag < maxVelMagThreads[i]) 
00963       maxVelMag = maxVelMagThreads[i];
00964 #endif
00965   delete [] maxVelMagThreads;
00966 
00967 
00968   // prepare density for an advection
00969   SWAP_POINTERS(_densityBig, _densityBigOld);
00970 
00971   // based on the maximum velocity present, see if we need to substep,
00972   // but cap the maximum number of substeps to 5
00973   const int maxSubSteps = 25;
00974   const int maxVel = 5;
00975   maxVelMag = sqrt(maxVelMag) * dt;
00976   int totalSubsteps = (int)(maxVelMag / (float)maxVel);
00977   totalSubsteps = (totalSubsteps < 1) ? 1 : totalSubsteps;
00978   // printf("totalSubsteps: %d\n", totalSubsteps);
00979   totalSubsteps = (totalSubsteps > maxSubSteps) ? maxSubSteps : totalSubsteps;
00980   const float dtSubdiv = dt / (float)totalSubsteps;
00981 
00982   // set boundaries of big velocity grid
00983   FLUID_3D::setZeroX(bigUx, _resBig, 0 , _resBig[2]); 
00984   FLUID_3D::setZeroY(bigUy, _resBig, 0 , _resBig[2]); 
00985   FLUID_3D::setZeroZ(bigUz, _resBig, 0 , _resBig[2]);
00986 
00987 #if PARALLEL==1
00988   int stepParts = threadval*2;  // Dividing parallelized sections into numOfThreads * 2 sections
00989   float partSize = (float)_zResBig/stepParts;   // Size of one part;
00990 
00991   if (partSize < 4) {stepParts = threadval;                 // If the slice gets too low (might actually slow things down, change it to larger
00992                     partSize = (float)_zResBig/stepParts;}
00993   if (partSize < 4) {stepParts = (int)(ceil((float)_zResBig/4.0f)); // If it's still too low (only possible on future systems with +24 cores), change it to 4
00994                     partSize = (float)_zResBig/stepParts;}
00995 #else
00996   int zBegin=0;
00997   int zEnd=_resBig[2];
00998 #endif
00999 
01000   // do the MacCormack advection, with substepping if necessary
01001   for(int substep = 0; substep < totalSubsteps; substep++)
01002   {
01003 
01004 #if PARALLEL==1
01005     #pragma omp parallel
01006     {
01007 
01008     #pragma omp for schedule(static,1)
01009     for (int i=0; i<stepParts; i++)
01010     {
01011         int zBegin = (int)((float)i*partSize + 0.5f);
01012         int zEnd = (int)((float)(i+1)*partSize + 0.5f);
01013 #endif
01014         FLUID_3D::advectFieldMacCormack1(dtSubdiv, bigUx, bigUy, bigUz, 
01015             _densityBigOld, tempBig1, _resBig, zBegin, zEnd);
01016 #if PARALLEL==1
01017     }
01018 
01019     #pragma omp barrier
01020 
01021     #pragma omp for schedule(static,1)
01022     for (int i=0; i<stepParts; i++)
01023     {
01024         int zBegin = (int)((float)i*partSize + 0.5f);
01025         int zEnd = (int)((float)(i+1)*partSize + 0.5f);
01026 #endif
01027         FLUID_3D::advectFieldMacCormack2(dtSubdiv, bigUx, bigUy, bigUz, 
01028             _densityBigOld, _densityBig, tempBig1, tempBig2, _resBig, NULL, zBegin, zEnd);
01029 #if PARALLEL==1
01030     }
01031     }
01032 #endif
01033 
01034     if (substep < totalSubsteps - 1) 
01035       SWAP_POINTERS(_densityBig, _densityBigOld);
01036   } // substep
01037 
01038   free(tempBig1);
01039   free(tempBig2);
01040   free(bigUx);
01041   free(bigUy);
01042   free(bigUz);
01043   free(_energy);
01044   free(highFreqEnergy);
01045   
01046   // wipe the density borders
01047   FLUID_3D::setZeroBorder(_densityBig, _resBig, 0 , _resBig[2]);
01048     
01049   // reset texture coordinates now in preparation for next timestep
01050   // Shouldn't do this before generating the noise because then the 
01051   // eigenvalues stored do not reflect the underlying texture coordinates
01052   resetTextureCoordinates(eigMin, eigMax);
01053 
01054   free(eigMin);
01055   free(eigMax);
01056   
01057   // output files
01058   // string prefix = string("./amplified.preview/density_bigxy_");
01059   // FLUID_3D::writeImageSliceXY(_densityBig, _resBig, _resBig[2]/2, prefix, _totalStepsBig, 1.0f);
01060   //string df3prefix = string("./df3/density_big_");
01061   //IMAGE::dumpDF3(_totalStepsBig, df3prefix, _densityBig, _resBig[0],_resBig[1],_resBig[2]);
01062   // string pbrtPrefix = string("./pbrt/density_big_");
01063   // IMAGE::dumpPBRT(_totalStepsBig, pbrtPrefix, _densityBig, _resBig[0],_resBig[1],_resBig[2]);
01064   
01065   _totalStepsBig++;
01066 }