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 ¤t = 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 ¤tPatch)
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 ¤tPatch,
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