git-svn-id: https://scorched3d.svn.sourceforge.net/svnroot/scorched3d/trunk/scorched...
[scorched3d/parasti.git] / src / client / water / Water2.cpp
blob91225082cc961e0fff75d03535bb5d8b67da7cb4
1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with Scorched3D; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ////////////////////////////////////////////////////////////////////////////////
21 #include <water/Water2.h>
22 #include <common/Vector.h>
23 #include <common/Vector4.h>
24 #include <common/Logger.h>
25 #include <common/ProgressCounter.h>
26 #include <common/OptionsTransient.h>
27 #include <client/ScorchedClient.h>
28 #include <landscapedef/LandscapeTex.h>
29 #include <landscapedef/LandscapeDefn.h>
30 #include <landscapemap/LandscapeMaps.h>
31 #include <graph/OptionsDisplay.h>
32 #include <GLEXT/GLState.h>
33 #include <GLEXT/GLStateExtension.h>
34 #include <image/ImageFactory.h>
35 #include <lang/LangResource.h>
36 #include "ocean_wave_generator.h"
38 #include <water/Water2Constants.h>
40 Water2::Water2()
44 Water2::~Water2()
49 Water2Patches &Water2::getPatch(float time)
51 unsigned int index = ((unsigned int)(time)) % generatedPatches_;
52 DIALOG_ASSERT(index < (unsigned int) generatedPatches_);
53 return patches_[index];
56 static float calculateError(Water2Points &displacement,
57 int x1, int x2, int y1, int y2,
58 float x1y1, float x2y2, float x1y2, float x2y1)
60 if (x2 - x1 <= 1) return 0.0f;
62 int midx = (x1 + x2) / 2;
63 int midy = (y1 + y2) / 2;
64 float actualheight = displacement.getPoint(midx, midy)[2];
66 float approxheight1 = (x1y1 + x2y2) / 2.0f;
67 float approxheight2 = (x1y2 + x2y1) / 2.0f;
68 float approxheight3 = (x1y1 + x1y2) / 2.0f;
69 float approxheight4 = (x1y1 + x2y1) / 2.0f;
70 float approxheight5 = (x1y2 + x2y2) / 2.0f;
71 float approxheight6 = (x2y1 + x2y2) / 2.0f;
73 float heightdiff1 = fabs(approxheight1 - actualheight);
74 float heightdiff2 = fabs(approxheight2 - actualheight);
76 float errorChild1 = calculateError(displacement,
77 x1, midx, y1, midy,
78 x1y1, approxheight1, approxheight3, approxheight4);
79 float errorChild2 = calculateError(displacement,
80 midx, x2, y1, midy,
81 approxheight4, approxheight6, approxheight1, x2y1);
82 float errorChild3 = calculateError(displacement,
83 x1, midx, midy, y2,
84 approxheight3, approxheight5, x1y2, approxheight2);
85 float errorChild4 = calculateError(displacement,
86 midx, x2, midy, y2,
87 approxheight2, x2y2, approxheight5, approxheight6);
89 float errorChildren = MAX(errorChild1, MAX(errorChild2, MAX(errorChild3, errorChild4)));
90 float totalError = MAX(errorChildren, MAX(heightdiff1, heightdiff2));
91 return totalError;
94 void Water2::generate(LandscapeTexBorderWater *water, ProgressCounter *counter)
96 if (counter) counter->setNewOp(LANG_RESOURCE("WATER_MOTION", "Water Motion"));
98 // Calculate water for position n
99 float windSpeed = ScorchedClient::instance()->
100 getOptionsTransient().getWindSpeed().asFloat() * 2.0f + 3.0f;
101 Vector windDir = ScorchedClient::instance()->
102 getOptionsTransient().getWindDirection().asVector();
103 if (windDir == Vector::getNullVector())
105 windDir = Vector(0.8f, 0.8f);
108 ocean_wave_generator<float>
109 owg(wave_resolution, // Resolution
110 windDir, // Wind dir
111 windSpeed, // wind speed m/s
112 float(wave_resolution) * (1e-8f), // scale factor for heights
113 float(wave_waterwidth), // wavetile_length
114 wave_tidecycle_time); // wave_tidecycle_time
116 // For each frame get the height data
117 generatedPatches_ = 0;
118 static Water2Points displacements[256];
119 for (unsigned i=0; i<wave_phases; i++)
121 if (counter) counter->setNewPercentage(float(i * 100) / float(wave_phases));
123 // Set frame number
124 float currentTime = wave_tidecycle_time * float(i) / float(wave_phases);
125 float timeMod = myfmod(currentTime, wave_tidecycle_time);
126 owg.set_time(timeMod);
128 // Calculate Zs
129 owg.compute_heights(displacements[i]);
131 // Calculate X,Ys
132 owg.compute_displacements(-2.0f, displacements[i]);
134 // Form a vector with the correct X,Y,Zs
135 for (int y=0; y<wave_resolution; y++)
137 for (int x=0; x<wave_resolution; x++)
139 Vector &point = displacements[i].getPoint(x, y);
140 point[2] += water->height.asFloat();
144 // Create the patches
145 patches_[i].generate(displacements[i], wave_resolution,
146 wave_patch_width, water->height.asFloat());
147 generatedPatches_++;
149 // Figure out the error for each LOD level for the water
150 // Do this for 1 64x64 patch as they should all be roughly similar
151 if (i == 0)
153 for (int j=0; j<=6; j++)
155 float error = 0.0f;
156 if (j>0)
158 int skip = 1 << j;
159 for (int y1=0; y1<wave_patch_width; y1+=skip)
161 for (int x1=0; x1<wave_patch_width; x1+=skip)
163 int x2 = x1 + skip;
164 int y2 = y1 + skip;
166 float x1y1 = displacements[i].getPoint(x1, y1)[2];
167 float x2y2 = displacements[i].getPoint(x2, y2)[2];
168 float x1y2 = displacements[i].getPoint(x1, y2)[2];
169 float x2y1 = displacements[i].getPoint(x2, y1)[2];
171 float thisError = calculateError(displacements[i],
172 x1, x2, y1, y2,
173 x1y1, x2y2, x1y2, x2y1);
174 error = MAX(error, thisError);
179 indexErrors_[j] = error;
183 // If we are not drawing water or no movement generate one patch
184 if (OptionsDisplay::instance()->getNoWaterMovement() ||
185 !OptionsDisplay::instance()->getDrawWater())
187 break;
191 if (indexs_.getNoLevels() == 0)
193 // Create the indexes
194 indexs_.generate(wave_patch_width, wave_patch_width, 2);
197 // compute amount of foam per vertex sample
198 LandscapeDefn &defn = *ScorchedClient::instance()->getLandscapeMaps().
199 getDefinitions().getDefn();
200 ImageHandle loadedFoam =
201 ImageFactory::loadImageHandle(S3D::getDataFile(water->foam.c_str()));
202 if (loadedFoam.getWidth() != wave_resolution ||
203 loadedFoam.getHeight() != wave_resolution)
205 S3D::dialogExit("Water2",
206 S3D::formatStringBuffer("Foam image size must be %ix%i",
207 wave_resolution, wave_resolution));
210 float aof[wave_resolution*wave_resolution];
211 memset(aof, 0, sizeof(float) * wave_resolution * wave_resolution);
213 float rndtab[37];
214 for (unsigned k = 0; k < 37; ++k) rndtab[k] = RAND;
216 // Waves, oaf part 1
217 if (GLStateExtension::hasShaders() &&
218 !OptionsDisplay::instance()->getNoWaterWaves() &&
219 !OptionsDisplay::instance()->getSimpleWaterShaders())
221 counter->setNewOp(LANG_RESOURCE("WATER_WAVES", "Water Waves"));
222 for (unsigned k = 0; k < wave_phases; ++k)
224 if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
225 Water2Points &wd = displacements[k % wave_phases];
226 generateAOF(wd, 0, rndtab, displacements, aof);
227 if (generatedPatches_ == 1) break;
231 // Waves, oaf part 2
232 counter->setNewOp(LANG_RESOURCE("WATER_EFFECTS", "Water Effects"));
233 for (unsigned k = 0; k < wave_phases; ++k)
235 if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
236 Water2Points &wd = displacements[k % wave_phases];
238 ImageHandle aofImage =
239 ImageFactory::createBlank(wave_resolution, wave_resolution, false, 0);
240 memcpy(aofImage.getBits(), loadedFoam.getBits(), wave_resolution * wave_resolution * 3);
242 // Add waves to AOF image
243 if (GLStateExtension::hasShaders() &&
244 !OptionsDisplay::instance()->getNoWaterWaves() &&
245 !OptionsDisplay::instance()->getSimpleWaterShaders())
247 generateAOF(wd, &aofImage, rndtab, displacements, aof);
249 else
251 unsigned char *bits = aofImage.getBits();
252 for (unsigned y = 0; y<wave_resolution; ++y)
254 for (unsigned x = 0; x<wave_resolution; ++x, bits+=3)
256 bits[0] = 0;
261 // Add transparency to AOF image
262 generateTransparency(wd, aofImage, defn);
264 // Save AOF image
265 Water2Patches &patches = patches_[k];
266 patches.getAOF().create(aofImage);
268 if (generatedPatches_ == 1) break;
272 void Water2::generateAOF(Water2Points &wd, ImageHandle *aofImage, float *rndtab,
273 Water2Points *displacements, float *aof)
275 // factor to build derivatives correctly
276 const float deriv_fac = wavetile_length_rcp * wave_resolution;
277 const float lambda = 1.0; // lambda has already been multiplied with x/y displacements...
278 const float decay = 4.0/wave_phases;
279 const float decay_rnd = 0.25/wave_phases;
280 const float foam_spawn_fac = 0.25;//0.125;
282 // compute for each sample how much foam is added (spawned)
283 for (unsigned y = 0; y < wave_resolution; ++y) {
284 unsigned ym1 = (y + wave_resolution - 1) & (wave_resolution-1);
285 unsigned yp1 = (y + 1) & (wave_resolution-1);
286 for (unsigned x = 0; x < wave_resolution; ++x) {
287 unsigned xm1 = (x + wave_resolution - 1) & (wave_resolution-1);
288 unsigned xp1 = (x + 1) & (wave_resolution-1);
290 Vector &xp1y = wd.getPoint(xp1, y);
291 Vector &xm1y = wd.getPoint(xm1, y);
292 Vector &xyp1 = wd.getPoint(x, yp1);
293 Vector &xym1 = wd.getPoint(x, ym1);
294 float dispx_dx = (xp1y[0] - xm1y[0]) * deriv_fac;
295 float dispx_dy = (xyp1[0] - xym1[0]) * deriv_fac;
296 float dispy_dx = (xp1y[1] - xm1y[1]) * deriv_fac;
297 float dispy_dy = (xyp1[1] - xym1[1]) * deriv_fac;
298 float Jxx = 1.0f + lambda * dispx_dx;
299 float Jyy = 1.0f + lambda * dispy_dy;
300 float Jxy = lambda * dispy_dx;
301 float Jyx = lambda * dispx_dy;
302 float J = Jxx*Jyy - Jxy*Jyx;
304 float foam_add = (J < 0.0f) ? ((J < -1.0f) ? 1.0f : -J) : 0.0f;
306 aof[y*wave_resolution+x] += foam_add * foam_spawn_fac;
308 // spawn foam also on neighbouring fields
309 aof[ym1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
310 aof[yp1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
311 aof[y*wave_resolution+xm1] += foam_add * foam_spawn_fac * 0.5f;
312 aof[y*wave_resolution+xp1] += foam_add * foam_spawn_fac * 0.5f;
316 // compute decay, depends on time with some randomness
317 unsigned ptr = 0;
318 for (unsigned y = 0; y < wave_resolution; ++y)
320 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr)
322 float aofVal = std::max(std::min(aof[ptr], 1.0f) -
323 (decay + decay_rnd * rndtab[(3*x + 5*y) % 37]), 0.0f);
325 aof[ptr] = aofVal;
327 if (aofImage)
329 aofImage->getBits()[ptr * 3 + 0] = (unsigned char) (255.0f * aofVal);
335 void Water2::generateTransparency(Water2Points &wd,
336 ImageHandle &aofImage, LandscapeDefn &defn)
338 unsigned ptr = 0;
339 for (unsigned y = 0; y < wave_resolution; ++y)
341 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr)
343 // Water height
344 Vector &points = wd.getPoint(x, y);
345 float waterHeight = points[2];
347 // Not quite right!! but it will do
348 int lx = int(float(x) * float(defn.getLandscapeWidth()) / float(wave_resolution));
349 int ly = int(float(y) * float(defn.getLandscapeHeight()) / float(wave_resolution));
350 float groundHeight = ScorchedClient::instance()->getLandscapeMaps().
351 getGroundMaps().getHeight(lx, ly).asFloat();
353 // Water depth
354 float waterDepth = waterHeight - groundHeight;
355 if (waterDepth < 0.0f) waterDepth = 0.0f;
356 else if (waterDepth > 10.0f) waterDepth = 10.0f;
358 // Store
359 unsigned char result = (unsigned char) (waterDepth * 25.0f);
360 aofImage.getBits()[ptr * 3 + 1] = result;