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