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/WaterWaves.h>
22 #include <water/WaterWaveDistance.h>
23 #include <water/Water2Patches.h>
24 #include <engine/Simulator.h>
25 #include <landscapemap/LandscapeMaps.h>
26 #include <lang/LangResource.h>
27 #include <client/ScorchedClient.h>
28 #include <graph/OptionsDisplay.h>
29 #include <common/Defines.h>
30 #include <GLEXT/GLStateExtension.h>
31 #include <GLEXT/GLInfo.h>
32 #include <image/ImageFactory.h>
33 #include <math.h>
34 
WaterWaves()35 WaterWaves::WaterWaves() :
36 	totalTime_(0.0f),
37 	wavesColor_(1.0f, 1.0f, 1.0f)
38 {
39 
40 }
41 
~WaterWaves()42 WaterWaves::~WaterWaves()
43 {
44 }
45 
generateWaves(float waterHeight,ProgressCounter * counter)46 void WaterWaves::generateWaves(float waterHeight, ProgressCounter *counter)
47 {
48 	paths1_.clear();
49 	paths2_.clear();
50 
51 	//if (OptionsDisplay::instance()->getNoWaves()) return;
52 	if (!OptionsDisplay::instance()->getDrawWater()) return;
53 
54 	int mapWidth = ScorchedClient::instance()->getLandscapeMaps().
55 		getGroundMaps().getLandscapeWidth();
56 	int mapHeight = ScorchedClient::instance()->getLandscapeMaps().
57 		getGroundMaps().getLandscapeHeight();
58 
59 	// Wave points
60 	WaterWaveContext context;
61 	context.pointCount = 0;
62 	context.removedCount = 0;
63 	context.pointsMult = 1;
64 	context.mapWidth = mapWidth;
65 	context.mapHeight = mapHeight;
66 	context.pointsWidth = mapWidth / context.pointsMult;
67 	context.pointsHeight = mapHeight / context.pointsMult;
68 	context.wavePoints = new bool[context.pointsWidth * context.pointsHeight];
69 	memset(context.wavePoints, 0,
70 		context.pointsWidth * context.pointsHeight * sizeof(bool));
71 
72 	// Find all of the points that are equal to a certain height (the water height)
73 	if (counter) counter->setNewOp(LANG_RESOURCE("CREATING_BREAKERS_1", "Creating Breakers 1"));
74 	findPoints(&context, waterHeight, counter);
75 
76 	// Find the list of points that are next to eachother
77 	if (counter) counter->setNewOp(LANG_RESOURCE("CREATING_BREAKERS_2", "Creating Breakers 2"));
78 	while (findNextPath(&context, waterHeight, counter)) {}
79 
80 	Image waves1 = ImageFactory::loadAlphaImage(
81 		S3D::eModLocation, "data/textures/waves.bmp");
82 	Image waves2 = ImageFactory::loadAlphaImage(
83 		S3D::eModLocation, "data/textures/waves2.bmp");
84 	wavesTexture1_.create(waves1);
85 	wavesTexture2_.create(waves2);
86 
87 	delete [] context.wavePoints;
88 }
89 
findPoints(WaterWaveContext * context,float waterHeight,ProgressCounter * counter)90 void WaterWaves::findPoints(WaterWaveContext *context,
91 	float waterHeight, ProgressCounter *counter)
92 {
93 	for (int y=context->pointsMult;
94 		y<context->mapHeight - context->pointsMult;
95 		y+=context->pointsMult)
96 	{
97 		if (counter) counter->setNewPercentage(float(y)/
98 			float(context->pointsHeight)*100.0f);
99 		for (int x=context->pointsMult;
100 			x<context->mapWidth - context->pointsMult;
101 			x+=context->pointsMult)
102 		{
103 			float height =
104 				ScorchedClient::instance()->getLandscapeMaps().
105 					getGroundMaps().getHeight(x, y).asFloat();
106 			if (height > waterHeight - 4.0f && height < waterHeight)
107 			{
108 				float height2=
109 					ScorchedClient::instance()->getLandscapeMaps().
110 					getGroundMaps().getHeight(x+context->pointsMult, y).asFloat();
111 				float height3=
112 					ScorchedClient::instance()->getLandscapeMaps().
113 					getGroundMaps().getHeight(x-context->pointsMult, y).asFloat();
114 				float height4=
115 					ScorchedClient::instance()->getLandscapeMaps().
116 					getGroundMaps().getHeight(x, y+context->pointsMult).asFloat();
117 				float height5=
118 					ScorchedClient::instance()->getLandscapeMaps().
119 					getGroundMaps().getHeight(x, y-context->pointsMult).asFloat();
120 
121 				if (height2 > waterHeight || height3 > waterHeight ||
122 					height4 > waterHeight || height5 > waterHeight)
123 				{
124 					{
125 						int a = x / context->pointsMult;
126 						int b = y / context->pointsMult;
127 						bool &pts = context->wavePoints[
128 							a + b * context->pointsWidth];
129 						if (!pts)
130 						{
131 							context->pointCount ++;
132 							pts = true;
133 						}
134 					}
135 				}
136 			}
137 		}
138 	}
139 }
140 
findNextPath(WaterWaveContext * context,float waterHeight,ProgressCounter * counter)141 bool WaterWaves::findNextPath(WaterWaveContext *context,
142 	float waterHeight, ProgressCounter *counter)
143 {
144 	std::vector<Vector> points;
145 
146 	for (int y=1; y<context->pointsHeight; y++)
147 	{
148 		for (int x=1; x<context->pointsWidth; x++)
149 		{
150 			if (context->wavePoints[x + y * context->pointsWidth])
151 			{
152 				findPath(context, points, x, y, counter);
153 				constructLines(context, waterHeight, points);
154 				return true;
155 			}
156 		}
157 	}
158 
159 	return false;
160 }
161 
findPath(WaterWaveContext * context,std::vector<Vector> & points,int x,int y,ProgressCounter * counter)162 void WaterWaves::findPath(WaterWaveContext *context,
163 	std::vector<Vector> &points,
164 	int x, int y, ProgressCounter *counter)
165 {
166 	if (x < 0 || x >= context->pointsWidth ||
167 		y < 0 || y >= context->pointsHeight)
168 	{
169 		return;
170 	}
171 
172 	context->removedCount ++;
173 	points.push_back(
174 		Vector(
175 			float(x * context->pointsMult),
176 			float(y * context->pointsMult),
177 			0.0f));
178 
179 	if (counter && context->removedCount % 50 == 0)
180 		counter->setNewPercentage(
181 			float(context->removedCount)/float(context->pointCount)*100.0f);
182 
183 	context->wavePoints[x + y * context->pointsWidth] = false;
184 	if (context->wavePoints[(x+1) + y * context->pointsWidth])
185 		findPath(context, points, x+1, y, counter);
186 	else if (context->wavePoints[(x-1) + y * context->pointsWidth])
187 		findPath(context, points, x-1, y, counter);
188 	else if (context->wavePoints[x + (y-1) * context->pointsWidth])
189 		findPath(context, points, x, y-1, counter);
190 	else if (context->wavePoints[x + (y+1) * context->pointsWidth])
191 		findPath(context, points, x, y+1, counter);
192 	else if (context->wavePoints[(x-1) + (y-1) * context->pointsWidth])
193 		findPath(context, points, x-1, y-1, counter);
194 	else if (context->wavePoints[(x-1) + (y+1) * context->pointsWidth])
195 		findPath(context, points, x-1, y+1, counter);
196 	else if (context->wavePoints[(x+1) + (y+1) * context->pointsWidth])
197 		findPath(context, points, x+1, y+1, counter);
198 	else if (context->wavePoints[(x+1) + (y-1) * context->pointsWidth])
199 		findPath(context, points, x+1, y-1, counter);
200 }
201 
constructLines(WaterWaveContext * context,float waterHeight,std::vector<Vector> & points)202 void WaterWaves::constructLines(WaterWaveContext *context,
203 	float waterHeight, std::vector<Vector> &points)
204 {
205 	Vector ptA;
206 	Vector ptB;
207 	Vector point = points.front();
208 	int dist = int(RAND * 10.0f + 1.0f);
209 	for (int i=0; i<(int)points.size(); i++)
210 	{
211 		Vector &current = points[i];
212 		int diffX = int(current[0]) - int(point[0]);
213 		int diffY = int(current[1]) - int(point[1]);
214 		int actualdist = diffX * diffX + diffY * diffY;
215 		if (actualdist  >= dist)
216 		{
217 			WaterWaveEntry entry;
218 			Vector grad = point-current;
219 			Vector perp = grad.get2DPerp().Normalize2D();
220 
221 			int newx = int(current[0] + perp[0] * 3.0f);
222 			int newy = int(current[1] + perp[1] * 3.0f);
223 			if (newx <= 0 || newy <= 0 ||
224 				newx >= context->mapWidth || newy >= context->mapHeight)
225 			{
226 			}
227 			else
228 			{
229 				if (ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getHeight(
230 					newx, newy).asFloat() > waterHeight)
231 				{
232 					perp =- perp;
233 					entry.ptA = current - perp / 3.0f;
234 					entry.ptB = point - perp / 3.0f;
235 					entry.perp = perp;
236 					entry.ptC = point + perp * 6.0f;
237 					entry.ptD = current + perp * 6.0f;
238 				}
239 				else
240 				{
241 					entry.ptA = point - perp / 3.0f;
242 					entry.ptB = current - perp / 3.0f;
243 					entry.perp = perp;
244 					entry.ptC = current + perp * 6.0f;
245 					entry.ptD = point + perp * 6.0f;
246 				}
247 
248 				if (RAND > 0.5f) paths1_.push_back(entry);
249 				else paths2_.push_back(entry);
250 			}
251 
252 			if (i > 1)
253 			{
254 				i-= 1;
255 				point = points[i];
256 			}
257 			else point = current;
258 
259 			dist = int(RAND * 10.0f + 1.0f);
260 		}
261 	}
262 }
263 
simulate(float frameTime)264 void WaterWaves::simulate(float frameTime)
265 {
266 	totalTime_ += frameTime / 2.0f;
267 	if (totalTime_ > 6.0f) totalTime_ = 0.0f;
268 }
269 
draw(Water2Patches & currentPatch)270 void WaterWaves::draw(Water2Patches &currentPatch)
271 {
272 	//if (OptionsDisplay::instance()->getNoWaves()) return;
273 
274 	Vector windDir =
275 		ScorchedClient::instance()->getSimulator().getWind().getWindStartingDirection().asVector();
276 	Vector windDirPerp = windDir.Normalize();
277 
278 	GLState state(GLState::TEXTURE_ON | GLState::BLEND_ON);
279 	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
280 	glDepthMask(GL_FALSE);
281 
282 	wavesTexture1_.draw(true);
283 	drawBoxes(currentPatch, totalTime_ + 0.0f, windDirPerp, paths1_);
284 	drawBoxes(currentPatch, totalTime_ + 2.0f, windDirPerp, paths1_);
285 	drawBoxes(currentPatch, totalTime_ + 4.0f, windDirPerp, paths1_);
286 
287 	wavesTexture2_.draw(true);
288 	drawBoxes(currentPatch, totalTime_ + 1.0f, windDirPerp, paths2_);
289 	drawBoxes(currentPatch, totalTime_ + 3.0f, windDirPerp, paths2_);
290 	drawBoxes(currentPatch, totalTime_ + 5.0f, windDirPerp, paths2_);
291 
292 	glDepthMask(GL_TRUE);
293 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
294 }
295 
drawBoxes(Water2Patches & currentPatch,float totalTime,Vector & windDirPerp,std::vector<WaterWaveEntry> & paths)296 void WaterWaves::drawBoxes(Water2Patches &currentPatch,
297 						   float totalTime, Vector &windDirPerp,
298 						   std::vector<WaterWaveEntry> &paths)
299 {
300 	float newTime = totalTime;
301 	if (newTime > 6.0f) newTime -= 6.0f;
302 
303 	// First set of boxes
304 	// Some set of magic to try to get it look kind of ok!
305 	float alpha = 1.0f;
306 	float frontlen = newTime + 0.2f;
307 	float endlen = newTime / 2.0f;
308 	if (newTime < 1.0f)
309 	{
310 		alpha = newTime;
311 	}
312 	if (newTime > 4.0f)
313 	{
314 		frontlen = 4.2f - (newTime - 4.0f) / 3.0f;
315 		endlen = 2.0f - (newTime - 4.0f) / 3.0f;
316 
317 		alpha = (2.0f - (newTime - 4.0f)) / 2.0f;
318 	}
319 	frontlen *= 2.0f;
320 	endlen *= 2.0f;
321 
322 	// Draw the actual texture boxes
323 	glColor4f(wavesColor_[0], wavesColor_[1], wavesColor_[2], alpha * 0.3f);
324 	glBegin(GL_QUADS);
325 		Vector ptA, ptB, ptC, ptD;
326 		std::vector<WaterWaveEntry>::iterator itor = paths.begin();
327 		std::vector<WaterWaveEntry>::iterator enditor = paths.end();
328 		for (; itor != enditor; ++itor)
329 		{
330 			WaterWaveEntry &p = *itor;
331 
332 			if (((p.perp[0] * windDirPerp[0]) + (p.perp[1] * windDirPerp[1])) > 0.0f)
333 				continue;
334 
335 			ptA = p.ptD - p.perp * frontlen;
336 			ptA[2] = currentPatch.getPoint(int(ptA[0]/2), int(ptA[1]/2))->z + 0.05f;
337 			ptB = p.ptC - p.perp * frontlen;
338 			ptB[2] = currentPatch.getPoint(int(ptB[0]/2), int(ptB[1]/2))->z + 0.05f;
339 
340 			ptC = p.ptC - p.perp * endlen;
341 			ptC[2] = currentPatch.getPoint(int(ptC[0]/2), int(ptC[1]/2))->z + 0.05f;
342 			ptD = p.ptD - p.perp * endlen;
343 			ptD[2] = currentPatch.getPoint(int(ptD[0]/2), int(ptD[1]/2))->z + 0.05f;
344 
345 			glTexCoord2f(1.0f, 1.0f);
346 			glVertex3fv(ptA);
347 			glTexCoord2f(0.0f, 1.0f);
348 			glVertex3fv(ptB);
349 			glTexCoord2f(0.0f, 0.0f);
350 			glVertex3fv(ptC);
351 			glTexCoord2f(1.0f, 0.0f);
352 			glVertex3fv(ptD);
353 		}
354 	glEnd();
355 	GLInfo::addNoTriangles((unsigned int) paths.size() - 2);
356 }
357