1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
4 // This file is part of Scorched3D.
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>
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
,
78 x1y1
, approxheight1
, approxheight3
, approxheight4
);
79 float errorChild2
= calculateError(displacement
,
81 approxheight4
, approxheight6
, approxheight1
, x2y1
);
82 float errorChild3
= calculateError(displacement
,
84 approxheight3
, approxheight5
, x1y2
, approxheight2
);
85 float errorChild4
= calculateError(displacement
,
87 approxheight2
, x2y2
, approxheight5
, approxheight6
);
89 float errorChildren
= MAX(errorChild1
, MAX(errorChild2
, MAX(errorChild3
, errorChild4
)));
90 float totalError
= MAX(errorChildren
, MAX(heightdiff1
, heightdiff2
));
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
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
));
124 float currentTime
= wave_tidecycle_time
* float(i
) / float(wave_phases
);
125 float timeMod
= myfmod(currentTime
, wave_tidecycle_time
);
126 owg
.set_time(timeMod
);
129 owg
.compute_heights(displacements
[i
]);
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());
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
153 for (int j
=0; j
<=6; j
++)
159 for (int y1
=0; y1
<wave_patch_width
; y1
+=skip
)
161 for (int x1
=0; x1
<wave_patch_width
; x1
+=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
],
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())
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
);
214 for (unsigned k
= 0; k
< 37; ++k
) rndtab
[k
] = RAND
;
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;
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
);
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)
261 // Add transparency to AOF image
262 generateTransparency(wd
, aofImage
, defn
);
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
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
);
329 aofImage
->getBits()[ptr
* 3 + 0] = (unsigned char) (255.0f
* aofVal
);
335 void Water2::generateTransparency(Water2Points
&wd
,
336 ImageHandle
&aofImage
, LandscapeDefn
&defn
)
339 for (unsigned y
= 0; y
< wave_resolution
; ++y
)
341 for (unsigned x
= 0; x
< wave_resolution
; ++x
, ++ptr
)
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();
354 float waterDepth
= waterHeight
- groundHeight
;
355 if (waterDepth
< 0.0f
) waterDepth
= 0.0f
;
356 else if (waterDepth
> 10.0f
) waterDepth
= 10.0f
;
359 unsigned char result
= (unsigned char) (waterDepth
* 25.0f
);
360 aofImage
.getBits()[ptr
* 3 + 1] = result
;