Water2.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2009
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <water/Water2.h>
00022 #include <common/Vector.h>
00023 #include <common/Vector4.h>
00024 #include <common/Logger.h>
00025 #include <common/ProgressCounter.h>
00026 #include <common/OptionsTransient.h>
00027 #include <client/ScorchedClient.h>
00028 #include <landscapedef/LandscapeTex.h>
00029 #include <landscapedef/LandscapeDefn.h>
00030 #include <landscapemap/LandscapeMaps.h>
00031 #include <graph/OptionsDisplay.h>
00032 #include <GLEXT/GLState.h>
00033 #include <GLEXT/GLStateExtension.h>
00034 #include <image/ImageFactory.h>
00035 #include <lang/LangResource.h>
00036 #include "ocean_wave_generator.h"
00037 
00038 #include <water/Water2Constants.h>
00039 
00040 Water2::Water2()
00041 {
00042 }
00043 
00044 Water2::~Water2()
00045 {
00046         
00047 }
00048 
00049 Water2Patches &Water2::getPatch(float time)
00050 {
00051         unsigned int index = ((unsigned int)(time)) % generatedPatches_;
00052         DIALOG_ASSERT(index < (unsigned int) generatedPatches_);
00053         return patches_[index];
00054 }
00055 
00056 static float calculateError(Water2Points &displacement,
00057         int x1, int x2, int y1, int y2,
00058         float x1y1, float x2y2, float x1y2, float x2y1)
00059 {
00060         if (x2 - x1 <= 1) return 0.0f;
00061 
00062         int midx = (x1 + x2) / 2;
00063         int midy = (y1 + y2) / 2;
00064         float actualheight = displacement.getPoint(midx, midy)[2];
00065 
00066         float approxheight1 = (x1y1 + x2y2) / 2.0f;
00067         float approxheight2 = (x1y2 + x2y1) / 2.0f;
00068         float approxheight3 = (x1y1 + x1y2) / 2.0f;
00069         float approxheight4 = (x1y1 + x2y1) / 2.0f;
00070         float approxheight5 = (x1y2 + x2y2) / 2.0f;
00071         float approxheight6 = (x2y1 + x2y2) / 2.0f;
00072 
00073         float heightdiff1 = fabs(approxheight1 - actualheight);
00074         float heightdiff2 = fabs(approxheight2 - actualheight);
00075         
00076         float errorChild1 = calculateError(displacement,
00077                 x1, midx, y1, midy,
00078                 x1y1, approxheight1, approxheight3, approxheight4);
00079         float errorChild2 = calculateError(displacement,
00080                 midx, x2, y1, midy,
00081                 approxheight4, approxheight6, approxheight1, x2y1);
00082         float errorChild3 = calculateError(displacement,
00083                 x1, midx, midy, y2,
00084                 approxheight3, approxheight5, x1y2, approxheight2);
00085         float errorChild4 = calculateError(displacement,
00086                 midx, x2, midy, y2,
00087                 approxheight2, x2y2, approxheight5, approxheight6);
00088 
00089         float errorChildren = MAX(errorChild1, MAX(errorChild2, MAX(errorChild3, errorChild4)));
00090         float totalError = MAX(errorChildren, MAX(heightdiff1, heightdiff2));
00091         return totalError;
00092 }
00093 
00094 void Water2::generate(LandscapeTexBorderWater *water, ProgressCounter *counter)
00095 {
00096         if (counter) counter->setNewOp(LANG_RESOURCE("WATER_MOTION", "Water Motion"));
00097 
00098         // Calculate water for position n
00099         float windSpeed = ScorchedClient::instance()->
00100                 getOptionsTransient().getWindSpeed().asFloat() * 2.0f + 3.0f;
00101         Vector windDir = ScorchedClient::instance()->
00102                 getOptionsTransient().getWindDirection().asVector();
00103         if (windDir == Vector::getNullVector())
00104         {
00105                 windDir = Vector(0.8f, 0.8f);
00106         }
00107 
00108         ocean_wave_generator<float> 
00109                 owg(wave_resolution, // Resolution
00110                         windDir, // Wind dir
00111                         windSpeed,  // wind speed m/s
00112                         float(wave_resolution) * (1e-8f), // scale factor for heights
00113                         float(wave_waterwidth), // wavetile_length
00114                         wave_tidecycle_time); // wave_tidecycle_time
00115 
00116         // For each frame get the height data
00117         generatedPatches_ = 0;
00118         static Water2Points displacements[256];
00119         for (unsigned i=0; i<wave_phases; i++) 
00120         {
00121                 if (counter) counter->setNewPercentage(float(i * 100) / float(wave_phases));
00122 
00123                 // Set frame number
00124                 float currentTime = wave_tidecycle_time * float(i) / float(wave_phases);
00125                 float timeMod = myfmod(currentTime, wave_tidecycle_time);
00126                 owg.set_time(timeMod);
00127 
00128                 // Calculate Zs
00129                 owg.compute_heights(displacements[i]);
00130 
00131                 // Calculate X,Ys
00132                 owg.compute_displacements(-2.0f, displacements[i]);
00133 
00134                 // Form a vector with the correct X,Y,Zs
00135                 for (int y=0; y<wave_resolution; y++)
00136                 {
00137                         for (int x=0; x<wave_resolution; x++)   
00138                         {
00139                                 Vector &point = displacements[i].getPoint(x, y);
00140                                 point[2] += water->height.asFloat();
00141                         }
00142                 }
00143 
00144                 // Create the patches
00145                 patches_[i].generate(displacements[i], wave_resolution, 
00146                         wave_patch_width, water->height.asFloat());
00147                 generatedPatches_++;
00148 
00149                 // Figure out the error for each LOD level for the water
00150                 // Do this for 1 64x64 patch as they should all be roughly similar
00151                 if (i == 0)
00152                 {
00153                         for (int j=0; j<=6; j++)
00154                         {
00155                                 float error = 0.0f;
00156                                 if (j>0)
00157                                 {
00158                                         int skip = 1 << j;
00159                                         for (int y1=0; y1<wave_patch_width; y1+=skip)
00160                                         {
00161                                                 for (int x1=0; x1<wave_patch_width; x1+=skip)
00162                                                 {
00163                                                         int x2 = x1 + skip;
00164                                                         int y2 = y1 + skip;
00165 
00166                                                         float x1y1 = displacements[i].getPoint(x1, y1)[2];
00167                                                         float x2y2 = displacements[i].getPoint(x2, y2)[2];
00168                                                         float x1y2 = displacements[i].getPoint(x1, y2)[2];
00169                                                         float x2y1 = displacements[i].getPoint(x2, y1)[2];
00170 
00171                                                         float thisError = calculateError(displacements[i],
00172                                                                 x1, x2, y1, y2,
00173                                                                 x1y1, x2y2, x1y2, x2y1);
00174                                                         error = MAX(error, thisError);
00175                                                 }
00176                                         }
00177                                 }
00178 
00179                                 indexErrors_[j] = error;
00180                         }
00181                 }
00182 
00183                 // If we are not drawing water or no movement generate one patch
00184                 if (OptionsDisplay::instance()->getNoWaterMovement() ||
00185                         !OptionsDisplay::instance()->getDrawWater())
00186                 {
00187                         break;
00188                 }
00189         }
00190 
00191         if (indexs_.getNoLevels() == 0)
00192         {
00193                 // Create the indexes
00194                 indexs_.generate(wave_patch_width, wave_patch_width, 2);
00195         }
00196 
00197         // compute amount of foam per vertex sample
00198         LandscapeDefn &defn = *ScorchedClient::instance()->getLandscapeMaps().
00199                 getDefinitions().getDefn();
00200         ImageHandle loadedFoam = 
00201                 ImageFactory::loadImageHandle(S3D::getDataFile(water->foam.c_str()));   
00202         if (loadedFoam.getWidth() != wave_resolution ||
00203                 loadedFoam.getHeight() != wave_resolution)
00204         {
00205                 S3D::dialogExit("Water2", 
00206                         S3D::formatStringBuffer("Foam image size must be %ix%i", 
00207                                 wave_resolution, wave_resolution));
00208         }
00209 
00210         float aof[wave_resolution*wave_resolution];
00211         memset(aof, 0, sizeof(float) * wave_resolution * wave_resolution);
00212 
00213         float rndtab[37];
00214         for (unsigned k = 0; k < 37; ++k) rndtab[k] = RAND;
00215 
00216         // Waves, oaf part 1
00217         if (GLStateExtension::hasShaders() &&
00218                 !OptionsDisplay::instance()->getNoWaterWaves() &&
00219                 !OptionsDisplay::instance()->getSimpleWaterShaders())
00220         {
00221                 counter->setNewOp(LANG_RESOURCE("WATER_WAVES", "Water Waves"));
00222                 for (unsigned k = 0; k < wave_phases; ++k) 
00223                 {
00224                         if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
00225                         Water2Points &wd = displacements[k % wave_phases];
00226                         generateAOF(wd, 0, rndtab, displacements, aof);
00227                         if (generatedPatches_ == 1) break;
00228                 }
00229         }
00230 
00231         // Waves, oaf part 2
00232         counter->setNewOp(LANG_RESOURCE("WATER_EFFECTS", "Water Effects"));
00233         for (unsigned k = 0; k < wave_phases; ++k) 
00234         {
00235                 if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
00236                 Water2Points &wd = displacements[k % wave_phases];
00237 
00238                 ImageHandle aofImage = 
00239                         ImageFactory::createBlank(wave_resolution, wave_resolution, false, 0);
00240                 memcpy(aofImage.getBits(), loadedFoam.getBits(), wave_resolution * wave_resolution * 3);
00241 
00242                 // Add waves to AOF image
00243                 if (GLStateExtension::hasShaders() &&
00244                         !OptionsDisplay::instance()->getNoWaterWaves() &&
00245                         !OptionsDisplay::instance()->getSimpleWaterShaders())
00246                 {
00247                         generateAOF(wd, &aofImage, rndtab, displacements, aof);
00248                 }
00249                 else
00250                 {
00251                         unsigned char *bits = aofImage.getBits();
00252                         for (unsigned y = 0; y<wave_resolution; ++y) 
00253                         {
00254                                 for (unsigned x = 0; x<wave_resolution; ++x, bits+=3) 
00255                                 {
00256                                         bits[0] = 0;
00257                                 }
00258                         }
00259                 }
00260 
00261                 // Add transparency to AOF image
00262                 generateTransparency(wd, aofImage, defn);
00263 
00264                 // Save AOF image
00265                 Water2Patches &patches = patches_[k];
00266                 patches.getAOF().create(aofImage);
00267 
00268                 if (generatedPatches_ == 1) break;
00269         }
00270 }
00271 
00272 void Water2::generateAOF(Water2Points &wd, ImageHandle *aofImage, float *rndtab, 
00273                                                  Water2Points *displacements, float *aof)
00274 {
00275         // factor to build derivatives correctly
00276         const float deriv_fac = wavetile_length_rcp * wave_resolution;
00277         const float lambda = 1.0; // lambda has already been multiplied with x/y displacements...
00278         const float decay = 4.0/wave_phases;
00279         const float decay_rnd = 0.25/wave_phases;
00280         const float foam_spawn_fac = 0.25;//0.125;
00281 
00282         // compute for each sample how much foam is added (spawned)
00283         for (unsigned y = 0; y < wave_resolution; ++y) {
00284                 unsigned ym1 = (y + wave_resolution - 1) & (wave_resolution-1);
00285                 unsigned yp1 = (y + 1) & (wave_resolution-1);
00286                 for (unsigned x = 0; x < wave_resolution; ++x) {
00287                         unsigned xm1 = (x + wave_resolution - 1) & (wave_resolution-1);
00288                         unsigned xp1 = (x + 1) & (wave_resolution-1);
00289 
00290                         Vector &xp1y = wd.getPoint(xp1, y);
00291                         Vector &xm1y = wd.getPoint(xm1, y);
00292                         Vector &xyp1 = wd.getPoint(x, yp1);
00293                         Vector &xym1 = wd.getPoint(x, ym1);
00294                         float dispx_dx = (xp1y[0] - xm1y[0]) * deriv_fac;
00295                         float dispx_dy = (xyp1[0] - xym1[0]) * deriv_fac;
00296                         float dispy_dx = (xp1y[1] - xm1y[1]) * deriv_fac;
00297                         float dispy_dy = (xyp1[1] - xym1[1]) * deriv_fac;
00298                         float Jxx = 1.0f + lambda * dispx_dx;
00299                         float Jyy = 1.0f + lambda * dispy_dy;
00300                         float Jxy = lambda * dispy_dx;
00301                         float Jyx = lambda * dispx_dy;
00302                         float J = Jxx*Jyy - Jxy*Jyx;
00303 
00304                         float foam_add = (J < 0.0f) ? ((J < -1.0f) ? 1.0f : -J) : 0.0f;                 
00305 
00306                         aof[y*wave_resolution+x] += foam_add * foam_spawn_fac;
00307 
00308                         // spawn foam also on neighbouring fields
00309                         aof[ym1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
00310                         aof[yp1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
00311                         aof[y*wave_resolution+xm1] += foam_add * foam_spawn_fac * 0.5f;
00312                         aof[y*wave_resolution+xp1] += foam_add * foam_spawn_fac * 0.5f;
00313                 }
00314         }
00315 
00316         // compute decay, depends on time with some randomness
00317         unsigned ptr = 0;
00318         for (unsigned y = 0; y < wave_resolution; ++y) 
00319         {
00320                 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr) 
00321                 {
00322                         float aofVal = std::max(std::min(aof[ptr], 1.0f) - 
00323                                 (decay + decay_rnd * rndtab[(3*x + 5*y) % 37]), 0.0f);
00324 
00325                         aof[ptr] = aofVal;
00326 
00327                         if (aofImage)
00328                         {
00329                                 aofImage->getBits()[ptr * 3 + 0] = (unsigned char) (255.0f * aofVal);
00330                         }
00331                 }
00332         }
00333 }
00334 
00335 void Water2::generateTransparency(Water2Points &wd, 
00336                                                                   ImageHandle &aofImage, LandscapeDefn &defn)
00337 {
00338         unsigned ptr = 0;
00339         for (unsigned y = 0; y < wave_resolution; ++y) 
00340         {
00341                 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr) 
00342                 {
00343                         // Water height
00344                         Vector &points = wd.getPoint(x, y);
00345                         float waterHeight = points[2];
00346 
00347                         // Not quite right!! but it will do
00348                         int lx = int(float(x) * float(defn.getLandscapeWidth()) / float(wave_resolution));
00349                         int ly = int(float(y) * float(defn.getLandscapeHeight()) / float(wave_resolution));
00350                         float groundHeight = ScorchedClient::instance()->getLandscapeMaps().
00351                                 getGroundMaps().getHeight(lx, ly).asFloat();
00352                         
00353                         // Water depth
00354                         float waterDepth = waterHeight - groundHeight;
00355                         if (waterDepth < 0.0f) waterDepth = 0.0f;
00356                         else if (waterDepth > 10.0f) waterDepth = 10.0f;
00357 
00358                         // Store
00359                         unsigned char result = (unsigned char) (waterDepth * 25.0f);
00360                         aofImage.getBits()[ptr * 3 + 1] = result;
00361                 }
00362         }
00363 }

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