1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
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.
10 //
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.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
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 <engine/Simulator.h>
28 #include <client/ScorchedClient.h>
29 #include <landscapedef/LandscapeTex.h>
30 #include <landscapedef/LandscapeDefn.h>
31 #include <landscapemap/LandscapeMaps.h>
32 #include <graph/OptionsDisplay.h>
33 #include <GLEXT/GLState.h>
34 #include <GLEXT/GLStateExtension.h>
35 #include <image/ImageFactory.h>
36 #include <lang/LangResource.h>
37 #include "ocean_wave_generator.h"
38
39 #include <water/Water2Constants.h>
40
Water2()41 Water2::Water2()
42 {
43 }
44
~Water2()45 Water2::~Water2()
46 {
47
48 }
49
getPatch(float time)50 Water2Patches &Water2::getPatch(float time)
51 {
52 unsigned int index = ((unsigned int)(time)) % generatedPatches_;
53 DIALOG_ASSERT(index < (unsigned int) generatedPatches_);
54 return patches_[index];
55 }
56
calculateError(Water2Points & displacement,int x1,int x2,int y1,int y2,float x1y1,float x2y2,float x1y2,float x2y1)57 static float calculateError(Water2Points &displacement,
58 int x1, int x2, int y1, int y2,
59 float x1y1, float x2y2, float x1y2, float x2y1)
60 {
61 if (x2 - x1 <= 1) return 0.0f;
62
63 int midx = (x1 + x2) / 2;
64 int midy = (y1 + y2) / 2;
65 float actualheight = displacement.getPoint(midx, midy)[2];
66
67 float approxheight1 = (x1y1 + x2y2) / 2.0f;
68 float approxheight2 = (x1y2 + x2y1) / 2.0f;
69 float approxheight3 = (x1y1 + x1y2) / 2.0f;
70 float approxheight4 = (x1y1 + x2y1) / 2.0f;
71 float approxheight5 = (x1y2 + x2y2) / 2.0f;
72 float approxheight6 = (x2y1 + x2y2) / 2.0f;
73
74 float heightdiff1 = fabs(approxheight1 - actualheight);
75 float heightdiff2 = fabs(approxheight2 - actualheight);
76
77 float errorChild1 = calculateError(displacement,
78 x1, midx, y1, midy,
79 x1y1, approxheight1, approxheight3, approxheight4);
80 float errorChild2 = calculateError(displacement,
81 midx, x2, y1, midy,
82 approxheight4, approxheight6, approxheight1, x2y1);
83 float errorChild3 = calculateError(displacement,
84 x1, midx, midy, y2,
85 approxheight3, approxheight5, x1y2, approxheight2);
86 float errorChild4 = calculateError(displacement,
87 midx, x2, midy, y2,
88 approxheight2, x2y2, approxheight5, approxheight6);
89
90 float errorChildren = MAX(errorChild1, MAX(errorChild2, MAX(errorChild3, errorChild4)));
91 float totalError = MAX(errorChildren, MAX(heightdiff1, heightdiff2));
92 return totalError;
93 }
94
generate(LandscapeTexBorderWater * water,ProgressCounter * counter)95 void Water2::generate(LandscapeTexBorderWater *water, ProgressCounter *counter)
96 {
97 if (counter) counter->setNewOp(LANG_RESOURCE("WATER_MOTION", "Water Motion"));
98
99 // Calculate water for position n
100 float windSpeed = ScorchedClient::instance()->
101 getSimulator().getWind().getWindSpeed().asFloat() * 2.0f + 3.0f;
102 Vector windDir = ScorchedClient::instance()->
103 getSimulator().getWind().getWindDirection().asVector();
104 if (windDir == Vector::getNullVector())
105 {
106 windDir = Vector(0.8f, 0.8f);
107 }
108
109 ocean_wave_generator<float>
110 owg(wave_resolution, // Resolution
111 windDir, // Wind dir
112 windSpeed, // wind speed m/s
113 float(wave_resolution) * (1e-8f), // scale factor for heights
114 float(wave_waterwidth), // wavetile_length
115 wave_tidecycle_time); // wave_tidecycle_time
116
117 // For each frame get the height data
118 generatedPatches_ = 0;
119 static Water2Points displacements[256];
120 for (unsigned i=0; i<wave_phases; i++)
121 {
122 if (counter) counter->setNewPercentage(float(i * 100) / float(wave_phases));
123
124 // Set frame number
125 float currentTime = wave_tidecycle_time * float(i) / float(wave_phases);
126 float timeMod = myfmod(currentTime, wave_tidecycle_time);
127 owg.set_time(timeMod);
128
129 // Calculate Zs
130 owg.compute_heights(displacements[i]);
131
132 // Calculate X,Ys
133 owg.compute_displacements(-2.0f, displacements[i]);
134
135 // Form a vector with the correct X,Y,Zs
136 for (int y=0; y<wave_resolution; y++)
137 {
138 for (int x=0; x<wave_resolution; x++)
139 {
140 Vector &point = displacements[i].getPoint(x, y);
141 point[2] += water->height.asFloat();
142 }
143 }
144
145 // Create the patches
146 patches_[i].generate(displacements[i], wave_resolution,
147 wave_patch_width, water->height.asFloat());
148 generatedPatches_++;
149
150 // Figure out the error for each LOD level for the water
151 // Do this for 1 64x64 patch as they should all be roughly similar
152 if (i == 0)
153 {
154 for (int j=0; j<=6; j++)
155 {
156 float error = 0.0f;
157 if (j>0)
158 {
159 int skip = 1 << j;
160 for (int y1=0; y1<wave_patch_width; y1+=skip)
161 {
162 for (int x1=0; x1<wave_patch_width; x1+=skip)
163 {
164 int x2 = x1 + skip;
165 int y2 = y1 + skip;
166
167 float x1y1 = displacements[i].getPoint(x1, y1)[2];
168 float x2y2 = displacements[i].getPoint(x2, y2)[2];
169 float x1y2 = displacements[i].getPoint(x1, y2)[2];
170 float x2y1 = displacements[i].getPoint(x2, y1)[2];
171
172 float thisError = calculateError(displacements[i],
173 x1, x2, y1, y2,
174 x1y1, x2y2, x1y2, x2y1);
175 error = MAX(error, thisError);
176 }
177 }
178 }
179
180 indexErrors_[j] = error;
181 }
182 }
183
184 // If we are not drawing water or no movement generate one patch
185 if (OptionsDisplay::instance()->getNoWaterMovement() ||
186 !OptionsDisplay::instance()->getDrawWater())
187 {
188 break;
189 }
190 }
191
192 if (indexs_.getNoLevels() == 0)
193 {
194 // Create the indexes
195 indexs_.generate(wave_patch_width, wave_patch_width, 2);
196 }
197
198 // compute amount of foam per vertex sample
199 LandscapeDefn &defn = *ScorchedClient::instance()->getLandscapeMaps().
200 getDefinitions().getDefn();
201 Image loadedFoam =
202 ImageFactory::loadImage(
203 S3D::eModLocation,
204 water->foam);
205 if (loadedFoam.getWidth() != wave_resolution ||
206 loadedFoam.getHeight() != wave_resolution)
207 {
208 S3D::dialogExit("Water2",
209 S3D::formatStringBuffer("Foam image size must be %ix%i",
210 wave_resolution, wave_resolution));
211 }
212
213 float aof[wave_resolution*wave_resolution];
214 memset(aof, 0, sizeof(float) * wave_resolution * wave_resolution);
215
216 float rndtab[37];
217 for (unsigned k = 0; k < 37; ++k) rndtab[k] = RAND;
218
219 // Waves, oaf part 1
220 if (GLStateExtension::hasShaders() &&
221 !OptionsDisplay::instance()->getNoWaterWaves() &&
222 !GLStateExtension::useSimpleShaders())
223 {
224 counter->setNewOp(LANG_RESOURCE("WATER_WAVES", "Water Waves"));
225 for (unsigned k = 0; k < wave_phases; ++k)
226 {
227 if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
228 Water2Points &wd = displacements[k % wave_phases];
229 generateAOF(wd, 0, rndtab, displacements, aof);
230 if (generatedPatches_ == 1) break;
231 }
232 }
233
234 // Waves, oaf part 2
235 counter->setNewOp(LANG_RESOURCE("WATER_EFFECTS", "Water Effects"));
236 for (unsigned k = 0; k < wave_phases; ++k)
237 {
238 if (counter) counter->setNewPercentage(float(k * 50) / float(wave_phases));
239 Water2Points &wd = displacements[k % wave_phases];
240
241 Image aofImage(wave_resolution, wave_resolution, 3, 0);
242 memcpy(aofImage.getBits(), loadedFoam.getBits(), wave_resolution * wave_resolution * 3);
243
244 // Add waves to AOF image
245 if (GLStateExtension::hasShaders() &&
246 !OptionsDisplay::instance()->getNoWaterWaves() &&
247 !GLStateExtension::useSimpleShaders())
248 {
249 generateAOF(wd, &aofImage, rndtab, displacements, aof);
250 }
251 else
252 {
253 unsigned char *bits = aofImage.getBits();
254 for (unsigned y = 0; y<wave_resolution; ++y)
255 {
256 for (unsigned x = 0; x<wave_resolution; ++x, bits+=3)
257 {
258 bits[0] = 0;
259 }
260 }
261 }
262
263 // Add transparency to AOF image
264 generateTransparency(wd, aofImage, defn);
265
266 // Save AOF image
267 Water2Patches &patches = patches_[k];
268 patches.getAOF().create(aofImage);
269
270 if (generatedPatches_ == 1) break;
271 }
272 }
273
generateAOF(Water2Points & wd,Image * aofImage,float * rndtab,Water2Points * displacements,float * aof)274 void Water2::generateAOF(Water2Points &wd, Image *aofImage, float *rndtab,
275 Water2Points *displacements, float *aof)
276 {
277 // factor to build derivatives correctly
278 const float deriv_fac = wavetile_length_rcp * wave_resolution;
279 const float lambda = 1.0; // lambda has already been multiplied with x/y displacements...
280 const float decay = 4.0/wave_phases;
281 const float decay_rnd = 0.25/wave_phases;
282 const float foam_spawn_fac = 0.25;//0.125;
283
284 // compute for each sample how much foam is added (spawned)
285 for (unsigned y = 0; y < wave_resolution; ++y) {
286 unsigned ym1 = (y + wave_resolution - 1) & (wave_resolution-1);
287 unsigned yp1 = (y + 1) & (wave_resolution-1);
288 for (unsigned x = 0; x < wave_resolution; ++x) {
289 unsigned xm1 = (x + wave_resolution - 1) & (wave_resolution-1);
290 unsigned xp1 = (x + 1) & (wave_resolution-1);
291
292 Vector &xp1y = wd.getPoint(xp1, y);
293 Vector &xm1y = wd.getPoint(xm1, y);
294 Vector &xyp1 = wd.getPoint(x, yp1);
295 Vector &xym1 = wd.getPoint(x, ym1);
296 float dispx_dx = (xp1y[0] - xm1y[0]) * deriv_fac;
297 float dispx_dy = (xyp1[0] - xym1[0]) * deriv_fac;
298 float dispy_dx = (xp1y[1] - xm1y[1]) * deriv_fac;
299 float dispy_dy = (xyp1[1] - xym1[1]) * deriv_fac;
300 float Jxx = 1.0f + lambda * dispx_dx;
301 float Jyy = 1.0f + lambda * dispy_dy;
302 float Jxy = lambda * dispy_dx;
303 float Jyx = lambda * dispx_dy;
304 float J = Jxx*Jyy - Jxy*Jyx;
305
306 float foam_add = (J < 0.0f) ? ((J < -1.0f) ? 1.0f : -J) : 0.0f;
307
308 aof[y*wave_resolution+x] += foam_add * foam_spawn_fac;
309
310 // spawn foam also on neighbouring fields
311 aof[ym1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
312 aof[yp1*wave_resolution+x] += foam_add * foam_spawn_fac * 0.5f;
313 aof[y*wave_resolution+xm1] += foam_add * foam_spawn_fac * 0.5f;
314 aof[y*wave_resolution+xp1] += foam_add * foam_spawn_fac * 0.5f;
315 }
316 }
317
318 // compute decay, depends on time with some randomness
319 unsigned ptr = 0;
320 for (unsigned y = 0; y < wave_resolution; ++y)
321 {
322 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr)
323 {
324 float aofVal = std::max(std::min(aof[ptr], 1.0f) -
325 (decay + decay_rnd * rndtab[(3*x + 5*y) % 37]), 0.0f);
326
327 aof[ptr] = aofVal;
328
329 if (aofImage)
330 {
331 aofImage->getBits()[ptr * 3 + 0] = (unsigned char) (255.0f * aofVal);
332 }
333 }
334 }
335 }
336
generateTransparency(Water2Points & wd,Image & aofImage,LandscapeDefn & defn)337 void Water2::generateTransparency(Water2Points &wd,
338 Image &aofImage, LandscapeDefn &defn)
339 {
340 unsigned ptr = 0;
341 for (unsigned y = 0; y < wave_resolution; ++y)
342 {
343 for (unsigned x = 0; x < wave_resolution; ++x, ++ptr)
344 {
345 // Water height
346 Vector &points = wd.getPoint(x, y);
347 float waterHeight = points[2];
348
349 // Not quite right!! but it will do
350 int lx = int(float(x) * float(defn.getLandscapeWidth()) / float(wave_resolution));
351 int ly = int(float(y) * float(defn.getLandscapeHeight()) / float(wave_resolution));
352 float groundHeight = ScorchedClient::instance()->getLandscapeMaps().
353 getGroundMaps().getHeight(lx, ly).asFloat();
354
355 // Water depth
356 float waterDepth = waterHeight - groundHeight;
357 if (waterDepth < 0.0f) waterDepth = 0.0f;
358 else if (waterDepth > 10.0f) waterDepth = 10.0f;
359
360 // Store
361 unsigned char result = (unsigned char) (waterDepth * 25.0f);
362 aofImage.getBits()[ptr * 3 + 1] = result;
363 }
364 }
365 }
366