ocean_wave_generator.h

Go to the documentation of this file.
00001 /*
00002 Danger from the Deep - Open source submarine simulation
00003 Copyright (C) 2003-2006  Thorsten Jordan, Luis Barrancos and others.
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU General Public License as published by
00007 the Free Software Foundation; either version 2 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU General Public License for more details.
00014 
00015 You should have received a copy of the GNU General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 */
00019 
00020 // an ocean wave generator (C)+(W) 2003 by Thorsten Jordan
00021 // realized algorithms of the paper "Simulating ocean water" by
00022 // Jerry Tessendorf.
00023 
00024 #ifndef OCEAN_WAVE_GENERATOR
00025 #define OCEAN_WAVE_GENERATOR
00026 
00027 #include <common/Vector.h>
00028 #ifdef __DARWIN__
00029 #include <FFTW3/fftw3.h>
00030 #else
00031 #include "fftw3.h"
00032 #endif
00033 #include <complex>
00034 #include <vector>
00035 
00036 #ifdef FFTW_USE_DOUBLE
00037 #define FFT_COMPLEX_TYPE fftw_complex
00038 #define FFT_REAL_TYPE double
00039 #define FFT_PLAN_TYPE fftw_plan
00040 #define FFT_CREATE_PLAN fftw_plan_dft_c2r_2d
00041 #define FFT_DELETE_PLAN fftw_destroy_plan
00042 #define FFT_EXECUTE_PLAN fftw_execute
00043 #else
00044 #define FFT_COMPLEX_TYPE fftwf_complex
00045 #define FFT_REAL_TYPE float
00046 #define FFT_PLAN_TYPE fftwf_plan
00047 #define FFT_CREATE_PLAN fftwf_plan_dft_c2r_2d
00048 #define FFT_DELETE_PLAN fftwf_destroy_plan
00049 #define FFT_EXECUTE_PLAN fftwf_execute
00050 #endif
00051 
00052 #define GRAVITY 10.0f
00053 
00054 #ifndef M_PI
00055 #define M_PI 3.1415926535897932 // should be in math.h, but not for Windows. *sigh*
00056 #endif
00057 
00058 ///\brief A generator class for ocean wave height data using a statistical model and the FFT.
00059 template <class T>
00060 class ocean_wave_generator
00061 {
00062         int N;          // grid size
00063         Vector W;       // wind direction
00064         T v;            // wind speed m/s
00065         T a;            // wave height scalar
00066         T Lm;           // tile size in m
00067         T w0;           // cycle time
00068         std::vector<std::complex<T> > h0tilde;
00069         std::vector<std::complex<T> > htilde;   // holds values for one fix time.
00070         
00071         ocean_wave_generator& operator= (const ocean_wave_generator& );
00072         ocean_wave_generator(const ocean_wave_generator& );
00073         static T myrnd();
00074         static std::complex<T> gaussrand();
00075         T phillips(Vector& K) const;
00076         std::complex<T> h0_tilde(Vector& K) const;
00077         void compute_h0tilde();
00078         std::complex<T> h_tilde(Vector& K, int kx, int ky, T time) const;
00079         void compute_htilde(T time);
00080         
00081         FFT_COMPLEX_TYPE *fft_in, *fft_in2;     // can't be a vector, since the type is an array
00082         FFT_REAL_TYPE *fft_out, *fft_out2;      // for sake of uniformity
00083         FFT_PLAN_TYPE plan, plan2, plan3;
00084         
00085 public:
00086         ocean_wave_generator(
00087                 int gridsize,
00088                 Vector& winddir,
00089                 T windspeed = T(20.0),
00090                 T waveheight = T(0.0001),       // fixme: compute that automatically from Lm, etc.?
00091                 T tilesize = T(100.0),
00092                 T cycletime = T(10.0)
00093         );
00094         // make a (smaller) copy, gridsize must be <= owg.gridsize,
00095         // clearlowfreq optionally clears lower frequencies, if it is < 0, it clears higher frequencies.
00096         ocean_wave_generator(const ocean_wave_generator<T>& owg, int gridsize,
00097                              int clearlowfreq = 0);
00098         void set_time(T time);  // call this before any compute_*() function
00099         void compute_heights(Water2Points& waveheights) const;
00100         // use this after height computation to avoid the overhead of fft normals
00101         //void compute_finite_normals(const std::vector<T>& heights, std::vector<Vector >& normals) const;
00102         //void compute_normals(std::vector<Vector >& wavenormals) const;
00103         // give negative values to use default factor, positive for displacement scaling
00104         void compute_displacements(const T& scalefac, Water2Points& wavedisplacements) const;
00105         ~ocean_wave_generator();
00106 };
00107 
00108 template <class T>
00109 T ocean_wave_generator<T>::myrnd()
00110 {
00111         return T(rand())/RAND_MAX;
00112 }
00113 
00114 template <class T>
00115 std::complex<T> ocean_wave_generator<T>::gaussrand()
00116 {
00117         T x1, x2, w;
00118         do {
00119                 x1 = T(2.0) * myrnd() - T(1.0);
00120                 x2 = T(2.0) * myrnd() - T(1.0);
00121                 w = x1 * x1 + x2 * x2;
00122         } while ( w >= T(1.0) );
00123         w = sqrt( (T(-2.0) * log( w ) ) / w );
00124         return std::complex<T>(x1 * w, x2 * w);
00125 }
00126 
00127 template <class T>
00128 T ocean_wave_generator<T>::phillips(Vector& K) const
00129 {
00130         T v2 = v * v;
00131         T k2 = K.MagnitudeSquared();
00132         if (k2 == T(0)) return T(0);
00133         T v4 = v2 * v2;
00134         T k4 = k2 * k2;
00135         T g2 = GRAVITY * GRAVITY;
00136         // note: Khat = K.normal() * W should be taken, but we use K
00137         // and divide |K * W|^2 by |K|^2 = k2 later.
00138         // note: a greater exponent could be used (e.g. 6) to align waves even more to the wind
00139         T KdotW = K.dotP(W);
00140         T KdotWhat = KdotW*KdotW/k2;
00141         T eterm = exp(-g2 / (k2 * v4)) / k4;
00142         T dampfac = T(1.0/100);
00143         T l2 = v4/g2 * dampfac*dampfac; // damping of very small waves
00144         T result = a * eterm * KdotWhat * exp(-k2*l2);  // only here the term "a" (wave height scalar) is used
00145         if (KdotW < T(0))       // filter out waves moving against the wind
00146                 result *= T(0.25);
00147         return result;
00148 }
00149 
00150 template <class T>
00151 std::complex<T> ocean_wave_generator<T>::h0_tilde(Vector& K) const
00152 {
00153         //in comparison to the water engine by ??? here some randomization is missing.
00154         //there this complex number is multiplied with a random sinus value
00155         //that means a random phase. But it doesn't seem to change the appearance much.
00156         // T f = sin(2*M_PI*T(rand())/RAND_MAX);
00157         std::complex<T> g = gaussrand();        // split in two expressions to make it compileable under VC6
00158         T p = sqrt(T(0.5) * phillips(K));
00159         return g * p;   // * f;
00160 }
00161 
00162 template <class T>
00163 void ocean_wave_generator<T>::compute_h0tilde()
00164 {
00165         const T pi2 = T(2.0*M_PI);
00166         // outer parts of arrays (x2,y2 away from zero) hold higher frequencies.
00167         // the significant frequencies are very close to zero, anything above
00168         // +-N/4 or so is only very high frequency
00169         for (int y = 0, y2 = -N/2; y <= N; ++y, ++y2) {
00170                 T Ky = pi2*y2/Lm;
00171                 for (int x = 0, x2 = -N/2; x <= N; ++x, ++x2) {
00172                         T Kx = pi2*x2/Lm;
00173                         Vector tmp(Kx, Ky);
00174                         h0tilde[y*(N+1)+x] = h0_tilde(tmp);
00175                 }
00176         }
00177 }
00178 
00179 template <class T>
00180 std::complex<T> ocean_wave_generator<T>::h_tilde(Vector& K, int kx, int ky, T time) const
00181 {
00182         std::complex<T> h0_tildeK = h0tilde[ky*(N+1)+kx];
00183         std::complex<T> h0_tildemKconj = conj(h0tilde[(N-ky)*(N+1)+(N-kx)]);
00184         // all frequencies should be multiples of one base frequency (see paper).
00185         T wK = sqrt(GRAVITY * K.Magnitude());
00186         T wK2 = floor(wK/w0)*w0;
00187         T xp = wK2 * time;
00188         T cxp = cos(xp);
00189         T sxp = sin(xp);
00190         return h0_tildeK * std::complex<T>(cxp, sxp) + h0_tildemKconj * std::complex<T>(cxp, -sxp);
00191 }
00192 
00193 template <class T>
00194 void ocean_wave_generator<T>::compute_htilde(T time)
00195 {
00196         const T pi2 = T(2.0*M_PI);
00197         for (int y = 0; y <= N/2; ++y) {
00198                 for (int x = 0; x < N; ++x) {
00199                         Vector K(pi2*(x-N/2)/Lm, pi2*(y-N/2)/Lm);
00200                         htilde[y*N+x] = h_tilde(K, x, y, time);
00201                 }
00202         }
00203 }
00204 
00205 template <class T>
00206 ocean_wave_generator<T>::ocean_wave_generator(
00207                 int gridsize,
00208                 Vector& winddir,
00209                 T windspeed,
00210                 T waveheight,
00211                 T tilesize,
00212                 T cycletime )
00213         : N(int(gridsize)), W(winddir.Normalize()), v(windspeed), a(waveheight), Lm(tilesize),
00214         w0(T(2.0*M_PI)/cycletime)
00215 {
00216         h0tilde.resize((N+1)*(N+1));
00217         compute_h0tilde();
00218         htilde.resize(N*(N/2+1));
00219         fft_in = new FFT_COMPLEX_TYPE[N*(N/2+1)];
00220         fft_in2 = new FFT_COMPLEX_TYPE[N*(N/2+1)];
00221         fft_out = new FFT_REAL_TYPE[N*N];
00222         fft_out2 = new FFT_REAL_TYPE[N*N];
00223         plan = FFT_CREATE_PLAN(N, N, fft_in, fft_out, 0);
00224         plan2 = FFT_CREATE_PLAN(N, N, fft_in2, fft_out2, 0);
00225 }
00226 /*
00227 template <class T>
00228 ocean_wave_generator<T>::ocean_wave_generator(const ocean_wave_generator<T>& owg, int gridsize,
00229                                               int clearlowfreq)
00230         : N(gridsize <= owg.N ? gridsize : owg.N), W(owg.W), v(owg.v), a(owg.a), Lm(owg.Lm), w0(owg.w0)
00231 {
00232         h0tilde.resize((N+1)*(N+1));
00233         //copy h0 tilde instead of computing it
00234         int offset = (owg.N - N) / 2;
00235         for (int y = 0; y <= N; ++y) {
00236                 for (int x = 0; x <= N; ++x) {
00237                         h0tilde[y*(N+1)+x] = owg.h0tilde[(y+offset)*(owg.N+1)+(x+offset)];
00238                 }
00239         }
00240         bool clearhigh = false;
00241         if (clearlowfreq < 0) {
00242                 clearhigh = true;
00243                 clearlowfreq = -clearlowfreq;
00244         }
00245         // clear lower frequencies if requested
00246         clearlowfreq = std::min(clearlowfreq, N/2+1);
00247         //clearlowfreq = std::max(clearlowfreq, 0);
00248         if (clearhigh) {
00249                 // clear high frequencies
00250                 for (int y = 0; y < clearlowfreq; ++y) {
00251                         for (int x = 0; x < clearlowfreq; ++x) {
00252                                 h0tilde[y*(N+1)+x] = 0;
00253                                 h0tilde[y*(N+1)+(N-x)] = 0;
00254                                 h0tilde[(N-y)*(N+1)+x] = 0;
00255                                 h0tilde[(N-y)*(N+1)+(N-x)] = 0;
00256                         }
00257                 }
00258         } else {
00259                 // clear low frequencies
00260                 for (int y = N/2+1-clearlowfreq; y <= N/2-1+clearlowfreq; ++y) {
00261                         for (int x = N/2+1-clearlowfreq; x <= N/2-1+clearlowfreq; ++x) {
00262                                 h0tilde[y*(N+1)+x] = 0;
00263                         }
00264                 }
00265         }
00266         htilde.resize(N*(N/2+1));
00267         fft_in = new FFT_COMPLEX_TYPE[N*(N/2+1)];
00268         fft_in2 = new FFT_COMPLEX_TYPE[N*(N/2+1)];
00269         fft_out = new FFT_REAL_TYPE[N*N];
00270         fft_out2 = new FFT_REAL_TYPE[N*N];
00271         plan = FFT_CREATE_PLAN(N, N, fft_in, fft_out, 0);
00272         plan2 = FFT_CREATE_PLAN(N, N, fft_in2, fft_out2, 0);
00273 }
00274 */
00275 
00276 template <class T>
00277 ocean_wave_generator<T>::~ocean_wave_generator()
00278 {
00279         FFT_DELETE_PLAN(plan);
00280         FFT_DELETE_PLAN(plan2);
00281         delete[] fft_in;
00282         delete[] fft_in2;
00283         delete[] fft_out;
00284         delete[] fft_out2;
00285 }
00286 
00287 template <class T>
00288 void ocean_wave_generator<T>::set_time(T time)
00289 {
00290         compute_htilde(time);
00291 }
00292 
00293 template <class T>
00294 void ocean_wave_generator<T>::compute_heights(Water2Points& waveheights) const
00295 {
00296         // this loop is a bit overhead, we could store htilde already in a fft_complex array
00297         // then we must transpose it, fftw has x*(N/2+1)+y, we use y*N+x
00298         // this overhead shouldn't matter.
00299         for (int y = 0; y <= N/2; ++y) {
00300                 for (int x = 0; x < N; ++x) {
00301                         const std::complex<T>& c = htilde[y*N+x];
00302                         int ptr = x*(N/2+1)+y;
00303                         fft_in[ptr][0] = c.real();
00304                         fft_in[ptr][1] = c.imag();
00305                 }
00306         }
00307 
00308         FFT_EXECUTE_PLAN(plan);
00309         
00310         // our kx,ky are in {-N/2...N/2}, but fft goes from {0...N-1}
00311         // so we have to add N/2 in the formulas, a term that can be seperated as exponential
00312         // term: exp(I*pi*(x+y)) that is equal to (-1)^(x+y), so we have to adjust
00313         // the sign at every second element in checkerboard form.
00314         // we copy the result to the output array in parallel.
00315         T signs[2] = { T(1), T(-1) };
00316         for (int y = 0; y < N; ++y)
00317                 for (int x = 0; x < N; ++x)
00318                 {
00319                         Vector &point = waveheights.getPoint(x, y);
00320                         point[2] = (T) fft_out[y*N+x] * signs[(x + y) & 1];
00321                 }
00322 }
00323 
00324 /*
00325 
00326 template <class T>
00327 void ocean_wave_generator<T>::compute_finite_normals(const std::vector<T>& heights,
00328                                                      std::vector<Vector >& normals) const
00329 {
00330         if (normals.size() != N*N)
00331                 normals.resize(N*N);
00332 
00333         typename std::vector<Vector >::iterator it = normals.begin();
00334         T sf = Lm/T(N);
00335         for (int y = 0; y < N; ++y) {
00336                 int y1 = (y+N-1)%N;
00337                 int y2 = (y+1)%N;
00338                 for (int x = 0; x < N; ++x) {
00339                         int x1 = (x+N-1)%N;
00340                         int x2 = (x+1)%N;
00341                         Vector a((x-1)*sf, (y+1)*sf, heights[y2*N+x1]);
00342                         Vector b((x-1)*sf, (y  )*sf, heights[y *N+x1]);
00343                         Vector c((x-1)*sf, (y-1)*sf, heights[y1*N+x1]);
00344                         Vector d((x  )*sf, (y-1)*sf, heights[y1*N+x ]);
00345                         Vector e((x+1)*sf, (y-1)*sf, heights[y1*N+x2]);
00346                         Vector f((x+1)*sf, (y  )*sf, heights[y *N+x2]);
00347                         Vector g((x+1)*sf, (y+1)*sf, heights[y2*N+x2]);
00348                         Vector h((x  )*sf, (y+1)*sf, heights[y2*N+x ]);
00349                         Vector ortho = (a.cross(b) + b.cross(c) + c.cross(d) + d.cross(e) + e.cross(f) + f.cross(g) + g.cross(h) + h.cross(a));
00350                         *it++ = ortho.normal();
00351                 }
00352         }
00353 }
00354 
00355 template <class T>
00356 void ocean_wave_generator<T>::compute_normals(std::vector<Vector >& wavenormals) const
00357 {
00358         const T pi2 = T(2.0)*T(M_PI);
00359         for (int y = 0; y <= N/2; ++y) {
00360                 for (int x = 0; x < N; ++x) {
00361                         const std::complex<T>& c = htilde[y*N+x];
00362                         Vector K(pi2*(x-N/2)/Lm, pi2*(y-N/2)/Lm);
00363                         int ptr = x*(N/2+1)+y;
00364                         fft_in[ptr][0] = -c.imag() * K.x;
00365                         fft_in[ptr][1] =  c.real() * K.x;
00366                         fft_in2[ptr][0] = -c.imag() * K.y;
00367                         fft_in2[ptr][1] =  c.real() * K.y;
00368                 }
00369         }
00370 
00371         FFT_EXECUTE_PLAN(plan);
00372         FFT_EXECUTE_PLAN(plan2);
00373         
00374         if (wavenormals.size() != N*N)
00375                 wavenormals.resize(N*N);
00376         T signs[2] = { T(1), T(-1) };
00377         unsigned ptr = 0;
00378         for (int y = 0; y < N; ++y) {
00379                 for (int x = 0; x < N; ++x) {
00380                         T s = signs[(x + y) & 1];
00381                         wavenormals[ptr] = Vector(-fft_out[ptr] * s, -fft_out2[ptr] * s, T(1)).normal();
00382                         ++ptr;
00383                 }
00384         }
00385 }
00386 */
00387 
00388 template <class T>
00389 void ocean_wave_generator<T>::compute_displacements(const T& scalefac,
00390                                                     Water2Points& wavedisplacements) const
00391 {
00392         for (int y = 0; y <= N/2; ++y) {
00393                 for (int x = 0; x < N; ++x) {
00394                         const std::complex<T>& c = htilde[y*N+x];
00395                         Vector K((x-N/2), (y-N/2));     // the factor 2*PI/Lm gets divided out later, so we don't need to multiply it.
00396                         T k = K.Magnitude();
00397                         Vector Kh;
00398                         if (k != 0)
00399                                 Kh = K * (T(1)/k);
00400                         else
00401                                 Kh[0] = Kh[1] = T(0);
00402                         int ptr = x*(N/2+1)+y;
00403                         fft_in[ptr][0] =  c.imag() * Kh[0];
00404                         fft_in[ptr][1] = -c.real() * Kh[0];
00405                         fft_in2[ptr][0] =  c.imag() * Kh[1];
00406                         fft_in2[ptr][1] = -c.real() * Kh[1];
00407                 }
00408         }
00409 
00410         FFT_EXECUTE_PLAN(plan);
00411         FFT_EXECUTE_PLAN(plan2);
00412         
00413         T signs[2] = { T(1), T(-1) };
00414         unsigned ptr = 0;
00415         for (int y = 0; y < N; ++y) {
00416                 for (int x = 0; x < N; ++x) {
00417                         T s = signs[(x + y) & 1];
00418 
00419                         Vector &point = wavedisplacements.getPoint(x, y);
00420                         point[0] = (T) fft_out[ptr] * s * scalefac;
00421                         point[1] = (T) fft_out2[ptr] * s * scalefac;
00422                         ++ptr;
00423                 }
00424         }
00425 }
00426 
00427 #endif

Generated on Mon Feb 16 15:14:48 2009 for Scorched3D by  doxygen 1.5.3