00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "std3d.h"
00027
00028 #include "water_height_map.h"
00029 #include "nel/misc/common.h"
00030 #include "nel/misc/debug.h"
00031 #include "nel/misc/vector_2f.h"
00032 #include <algorithm>
00033 #include <math.h>
00034
00035
00036
00037 namespace NL3D
00038 {
00039
00040
00041
00042 CWaterHeightMap::CWaterHeightMap() : Date(-1),
00043 _WavesEnabled(false),
00044 _Damping(0.97f),
00045 _FilterWeight(4),
00046 _UnitSize(0.6f),
00047 _WaveIntensity(0),
00048 _WavePeriod(0),
00049 _WaveImpulsionRadius(3),
00050 _BorderWaves(true),
00051 _EmitEllapsedTime(0),
00052 _PropagateEllapsedTime(0),
00053 _PropagationTime(0.10f),
00054 _X(0),
00055 _Y(0),
00056 _NewX(0),
00057 _NewY(0),
00058 _CurrMap(0),
00059 _Size(0)
00060 {
00061 }
00062
00063
00064
00065
00066 void CWaterHeightMap::setPropagationTime(float time)
00067 {
00068 _PropagationTime = time;
00069 _PropagateEllapsedTime = 0;
00070 for (uint k = 0; k < NumWaterMap; ++k)
00071 {
00072 clearArea(k, 0, 0, _Size << 1, _Size << 1);
00073 }
00074 }
00075
00076
00077
00078 void CWaterHeightMap::updateUserPos()
00079 {
00080 const sint x = _NewX;
00081 const sint y = _NewY;
00082
00083 nlassert(_Size != 0);
00084 if ((uint) x == _X && (uint) y == _Y) return;
00085 if ((uint) abs(x - _X) < _Size && (uint) abs(y - _Y) < _Size)
00086 {
00087
00088
00089 sint XDivSize;
00090 if ((sint) _X >= 0) XDivSize = (sint) _X / (sint) _Size;
00091 else XDivSize = ((sint) (_X + 1) / (sint) _Size) - 1;
00092
00093 sint YDivSize;
00094 if ((sint) _Y >= 0) YDivSize = (sint) _Y / (sint) _Size;
00095 else YDivSize = ((sint) (_Y + 1) / (sint) _Size) - 1;
00096
00097 sint xDivSize;
00098 if (x >= 0) xDivSize = (sint) x / (sint) _Size;
00099 else xDivSize = ((sint) (x + 1) / (sint) _Size) - 1;
00100
00101 sint yDivSize;
00102 if (y >= 0) yDivSize = (sint) y / (sint) _Size;
00103 else yDivSize = ((sint) (y + 1) / (sint) _Size) - 1;
00104
00105
00106 if (xDivSize != XDivSize || yDivSize != YDivSize)
00107 {
00108 sint left = std::max(x, (sint) _X);
00109 sint top = std::max(y, (sint) _Y);
00110 sint right = std::min(x + (sint) _Size, (sint) (_X + _Size));
00111 sint bottom = std::min(y + (sint) _Size, (sint) (_Y + _Size));
00112 sint offsetX, offsetY;
00113 if (xDivSize != XDivSize)
00114 {
00115 offsetX = xDivSize < XDivSize ? _Size : -(sint)_Size;
00116 }
00117 else
00118 {
00119 offsetX = 0;
00120 }
00121
00122 if (yDivSize != YDivSize)
00123 {
00124 offsetY = yDivSize < YDivSize ? _Size : -(sint)_Size;
00125 }
00126 else
00127 {
00128 offsetY = 0;
00129 }
00130
00131 sint orgX = _Size * XDivSize;
00132 sint orgY = _Size * YDivSize;
00133 for (uint k = 0; k < NumWaterMap; ++k)
00134 {
00135 makeCpy(k, (uint) (left - orgX + offsetX) , (uint) (top - orgY + offsetY),
00136 (uint) (left - orgX), (uint) (top - orgY),
00137 (uint) (right - left), (uint) (bottom - top));
00138 }
00139 }
00140
00141 sint newOrgX = _Size * xDivSize;
00142 sint newOrgY = _Size * yDivSize;
00143
00144 if (x == (sint) _X)
00145 {
00146 if (y < (sint) _Y)
00147 {
00148
00149 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
00150 }
00151 else
00152 {
00153 clearZone(x - newOrgX, y + _Size - newOrgY, _Size, y - _Y);
00154 }
00155 }
00156 else
00157 {
00158 if (x > (sint) _X)
00159 {
00160 if (y == (sint) _Y)
00161 {
00162 clearZone(_X + _Size - newOrgX, y - newOrgY, x - _X, _Size);
00163 }
00164 else if (y < (sint) _Y)
00165 {
00166 clearZone(_X + _Size - newOrgX, _Y - newOrgY, x - _X, _Size - (_Y - y));
00167 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
00168 }
00169 else
00170 {
00171 clearZone(_X + _Size - newOrgX, y - newOrgY, x - _X, _Size - (y - _Y));
00172 clearZone(x - newOrgX, _Y + _Size - newOrgY, _Size, y - _Y);
00173 }
00174 }
00175 else
00176 {
00177 if (y == (sint) _Y)
00178 {
00179 clearZone(x - newOrgX, y - newOrgY, _X - x, _Size);
00180 }
00181 else if (y < (sint) _Y)
00182 {
00183 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
00184 clearZone(x - newOrgX, _Y - newOrgY, _X - x, _Size - (_Y - y));
00185 }
00186 else
00187 {
00188 clearZone(x - newOrgX, y - newOrgY, _X - x, _Size - (y -_Y));
00189 clearZone(x - newOrgX, _Y + _Size - newOrgY, _Size, y - _Y);
00190 }
00191 }
00192 }
00193
00194 }
00195 else
00196 {
00197
00198
00199 uint px = _X % _Size;
00200 uint py = _Y % _Size;
00201 clearZone(px, py, _Size, _Size);
00202 }
00203
00204 _X = (uint) x;
00205 _Y = (uint) y;
00206 }
00207
00208
00209
00210
00211
00212 void CWaterHeightMap::animatePart(float startTime, float endTime)
00213 {
00214 if (endTime < 0.5f * _PropagationTime)
00215 {
00216
00217 propagate((uint) (_Size * 2.f * startTime / _PropagationTime), (uint) (_Size * 2.f * endTime / _PropagationTime));
00218 }
00219 else
00220 {
00221
00222 if (startTime < 0.5f * _PropagationTime)
00223 {
00224 propagate((uint) (_Size * 2.f * startTime / _PropagationTime), _Size);
00225 filter(0, (uint) (_Size * 2.f * (endTime / _PropagationTime - 0.5f)));
00226 }
00227 else
00228 {
00229 filter((uint) (_Size * 2.f * (startTime / _PropagationTime - 0.5f)), (uint) (_Size * 2.f * (endTime / _PropagationTime - 0.5f)));
00230 }
00231 }
00232 }
00233
00234
00235
00236 void CWaterHeightMap::animate(float deltaT)
00237 {
00238 if (deltaT < 0) deltaT = 0;
00239 if (deltaT > _PropagationTime)
00240 {
00241 animatePart(0, _PropagationTime);
00242 swapBuffers(deltaT);
00243 _PropagateEllapsedTime = 0;
00244 }
00245 else
00246 {
00247 const float endTime = _PropagateEllapsedTime + deltaT;
00248 const float startTime = _PropagateEllapsedTime;
00249
00250 if (endTime < _PropagationTime)
00251 {
00252 animatePart(startTime, endTime);
00253 _PropagateEllapsedTime = endTime;
00254 }
00255 else
00256 {
00257 animatePart(startTime, _PropagationTime);
00258 swapBuffers(deltaT);
00259
00260
00261 _PropagateEllapsedTime = 0 ;
00262 }
00263 }
00264 animateWaves(deltaT);
00265 }
00266
00267
00268
00269 void CWaterHeightMap::setSize(uint32 size)
00270 {
00271 nlassert(size > 4);
00272 _Size = size;
00273 for (uint k = 0; k < NumWaterMap; ++k)
00274 {
00275 _Map[k].resize(4 * _Size * _Size);
00276 clearArea(k, 0, 0, _Size << 1, _Size << 1);
00277 }
00278
00279 }
00280
00281
00282
00283 void CWaterHeightMap::makeCpy(uint buffer, uint dX, uint dY, uint sX, uint sY, uint width, uint height)
00284 {
00285 if (width == 0 || height == 0) return;
00286 nlassert(dX <= (2 * _Size));
00287 nlassert(dY <= (2 * _Size));
00288 nlassert(sX <= (2 * _Size));
00289 nlassert(sY <= (2 * _Size));
00290 nlassert(dX + width <= 2 * _Size);
00291 nlassert(sX + width <= 2 * _Size);
00292 nlassert(dY + height <= 2 * _Size);
00293 nlassert(sY + height <= 2 * _Size);
00294
00295 sint stepY;
00296 float *src, *dest;
00297
00298 const sint stride = _Size << 1;
00299 if (dY <= sY)
00300 {
00301 stepY = stride;
00302 src = &_Map[buffer][sX + sY * stride];
00303 dest = &_Map[buffer][dX + dY * stride];
00304 }
00305 else
00306 {
00307 stepY = -stride;
00308 src = &_Map[buffer][sX + (sY + height - 1) * stride];
00309 dest = &_Map[buffer][dX + (dY + height - 1) * stride];
00310 }
00311
00312 sint k = height;
00313 do
00314 {
00315 if (dest < src)
00316 {
00317 std::copy(src, src + width, dest);
00318 }
00319 else
00320 {
00321 float *rSrc = src + width;
00322 float *rDest = dest + width;
00323 do
00324 {
00325 --rSrc;
00326 --rDest;
00327 *rDest = *rSrc;
00328 }
00329 while (rSrc != src);
00330 }
00331 src += stepY;
00332 dest += stepY;
00333 }
00334 while (--k);
00335 }
00336
00337
00338
00339 void CWaterHeightMap::setUserPos(sint x, sint y)
00340 {
00341 _NewX = x;
00342 _NewY = y;
00343 }
00344
00345
00346
00347 void CWaterHeightMap::getUserPos(sint &x, sint &y) const
00348 {
00349 x = (sint) _X; y = (sint) _Y;
00350 }
00351
00352
00353
00354
00355
00356 void CWaterHeightMap::propagate(uint start, uint end)
00357 {
00358 start = std::max(1u, start);
00359 end = std::min((uint) (_Size - 1), end);
00360 const float damping = _Damping;
00361 clearBorder(0);
00362 clearBorder(1);
00363 nlassert(_Size != 0);
00364 sint x, y;
00365 uint px = _X % _Size;
00366 uint py = _Y % _Size;
00367 sint offset = px + 1 + ((py + start) * (_Size << 1));
00368
00369 float *buf2 = &_Map[ (_CurrMap + (NumWaterMap - 1)) % NumWaterMap][offset];
00370 float *buf1 = &_Map[_CurrMap][offset];
00371 float *dest = &_Map[(_CurrMap + 1) % NumWaterMap][offset];
00372
00373 const sint sizeX2 = _Size << 1;
00374 y = end - start;
00375 if (y <= 0) return;
00376 do
00377 {
00378 x = _Size - 2;
00379 do
00380 {
00381 *dest = damping * ( 0.5f * (buf1[1] + buf1[-1] + buf1[sizeX2] + buf1[- sizeX2]) - *buf2);
00382 ++buf1;
00383 ++buf2;
00384 ++dest;
00385 }
00386 while (--x);
00387 buf1 = buf1 + _Size + 2;
00388 buf2 = buf2 + _Size + 2;
00389 dest = dest + _Size + 2;
00390 }
00391 while (--y);
00392
00393 }
00394
00395
00396
00397
00398
00399 void CWaterHeightMap::filter(uint start, uint end)
00400 {
00401 start = std::max(1u, start);
00402 end = std::min((uint) (_Size - 1), end);
00403 const float blurCoeff = _FilterWeight;
00404 nlassert(_Size != 0);
00405 sint x, y;
00406 uint px = _X % _Size;
00407 uint py = _Y % _Size;
00408 sint offset = px + 1 + ((py + start) * (_Size << 1));
00409 float *buf = &_Map[ (_CurrMap + 1) % NumWaterMap ][offset];
00410
00411 y = end - start;
00412 if (y <= 0) return;
00413 const float totalBlurCoeff = (1.f / (4.f + blurCoeff));
00414 const sint sizeX2 = _Size << 1;
00415 do
00416 {
00417 x = _Size - 2;
00418 do
00419 {
00420 *buf = totalBlurCoeff * (*buf * blurCoeff
00421 + buf[1]
00422 + buf[-1]
00423 + buf[sizeX2]
00424 + buf[- sizeX2]
00425 );
00426
00427
00428
00429
00430 ++buf;
00431
00432 }
00433 while (--x);
00434 buf += _Size + 2;
00435
00436 }
00437 while (--y);
00438 }
00439
00440
00441
00442 void CWaterHeightMap::animateWaves(float deltaT)
00443 {
00444 if (_WavesEnabled)
00445 {
00446 uint numWaves;
00447 if (_WavePeriod == 0)
00448 {
00449 numWaves = 1;
00450 }
00451 else
00452 {
00453 _EmitEllapsedTime += deltaT;
00454 if (_EmitEllapsedTime > _WavePeriod)
00455 {
00456 numWaves = (uint) (_EmitEllapsedTime / _WavePeriod);
00457 _EmitEllapsedTime -= numWaves * _WavePeriod;
00458 if (numWaves > 10) numWaves = 10;
00459 }
00460 else
00461 {
00462 numWaves = 0;
00463 }
00464 }
00465
00466 uint k;
00467
00468 if (!_BorderWaves)
00469 {
00470 if (_WaveIntensity != 0)
00471 {
00472 for (k = 0; k < numWaves; ++k)
00473 {
00474 perturbate(_NewX + rand() % _Size, _NewY + rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
00475 }
00476 }
00477 }
00478 else
00479 {
00480 switch(rand() & 3)
00481 {
00482 case 0:
00483 for (k = 0; k < numWaves; ++k)
00484 {
00485 perturbate(_NewX + (uint) rand() % _Size, _NewY, _WaveImpulsionRadius, _WaveIntensity);
00486 }
00487 break;
00488 case 1:
00489 for (k = 0; k < numWaves; ++k)
00490 {
00491 perturbate(_NewX + (uint) rand() % _Size, _NewY + _Size - 1, _WaveImpulsionRadius, _WaveIntensity);
00492 }
00493 break;
00494 case 2:
00495 for (k = 0; k < numWaves; ++k)
00496 {
00497 perturbate(_NewX + _Size - 1, _NewY + (uint) rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
00498 }
00499 break;
00500 case 3:
00501 for (k = 0; k < numWaves; ++k)
00502 {
00503 perturbate(_NewX, _NewY + (uint) rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
00504 }
00505 break;
00506 }
00507
00508 }
00509 }
00510 }
00511
00512
00513
00514 void CWaterHeightMap::swapBuffers(float deltaT)
00515 {
00516 updateUserPos();
00517 _CurrMap = (_CurrMap + 1) % NumWaterMap;
00518 }
00519
00520
00521
00522
00523 void CWaterHeightMap::clearZone(sint x, sint y, sint width, sint height)
00524 {
00525 for (uint k = 0; k < NumWaterMap; ++k)
00526 {
00527 clearArea(k, x, y, width, height);
00528 }
00529 }
00530
00531
00532
00533 void CWaterHeightMap::clearArea(uint8 currMap, sint x, sint y, sint width, sint height)
00534 {
00535 nlassert(_Size > 1);
00536 nlassert(width >= 0);
00537 nlassert(height >= 0);
00538 uint sizex2 = _Size << 1;
00539
00540 if (x < 0)
00541 {
00542 width += x;
00543 x = 0;
00544 if (width <= 0) return;
00545 }
00546 if (y < 0)
00547 {
00548 height += y;
00549 y = 0;
00550 if (height <= 0) return;
00551 }
00552 if (x + width > (sint) sizex2)
00553 {
00554 width = width - (x + width - sizex2);
00555 }
00556
00557 if (y + height > (sint) sizex2)
00558 {
00559 height = height - (y + height - sizex2);
00560 }
00561
00562 float *dest = &*(_Map[ currMap ].begin() + x + (_Size << 1) * y);
00563 do
00564 {
00565 std::fill(dest, dest + width, 0.f);
00566 dest += (_Size << 1);
00567 }
00568 while (-- height);
00569 }
00570
00571
00572
00573
00574
00575 void CWaterHeightMap::perturbate(sint x, sint y, sint radius, float intensity)
00576 {
00577 nlassert(_Size != 0);
00578 nlassert(radius > 0);
00579 sint orgX = _X - _X % _Size;
00580 sint orgY = _Y - _Y % _Size;
00581 TFloatVect &map = _Map[(_CurrMap + 1) % NumWaterMap];
00582 const uint sizeX2 = _Size << 1;
00583 for (sint px = -radius + 1; px < radius; ++px)
00584 {
00585 for (sint py = -radius + 1; py < radius; ++py)
00586 {
00587 if ((uint) (x + px - orgX) < sizeX2
00588 && (uint) (y + py - orgY) < sizeX2)
00589 {
00590
00591 float dist = ((float) radius - sqrtf((float) (px * px + py * py ))) / (float) radius;
00592 float v = dist < radius ? intensity * cosf(dist * (float) NLMISC::Pi * 0.5f) : 0.f;
00593 map[x + px - orgX + sizeX2 * (y + py - orgY)] = v;
00594 }
00595 }
00596 }
00597 }
00598
00599
00600
00601 void CWaterHeightMap::perturbate(const NLMISC::CVector2f &pos, float strenght, float radius)
00602 {
00603 const float invUnitSize = 1.f / _UnitSize;
00604 perturbate((sint) (pos.x * invUnitSize), (sint) (pos.y * invUnitSize), (sint) radius, strenght);
00605 }
00606
00607
00608
00609 void CWaterHeightMap::perturbatePoint(sint x, sint y, float intensity)
00610 {
00611 sint orgX = _X - _X % _Size;
00612 sint orgY = _Y - _Y % _Size;
00613 uint X = (uint) (x - orgX);
00614 uint Y = (uint) (y - orgY);
00615 if (X < (_Size << 1)
00616 && Y < (_Size << 1)
00617 )
00618 {
00619 const uint sizex2 = _Size << 1;
00620 TFloatVect &map = _Map[(_CurrMap + 1) % NumWaterMap];
00621 map[X + sizex2 * Y] = intensity;
00622 }
00623 }
00624
00625
00626
00627 void CWaterHeightMap::perturbatePoint(const NLMISC::CVector2f &pos, float strenght)
00628 {
00629 const float invUnitSize = 1.f / _UnitSize;
00630 perturbatePoint((sint) (pos.x * invUnitSize), (sint) (pos.y * invUnitSize), strenght);
00631 }
00632
00633
00634
00635 void CWaterHeightMap::clearBorder(uint currMap)
00636 {
00637 float *map = &_Map[currMap][0];
00638 uint sizex2 = _Size << 1;
00639
00640
00641
00642 float *up = &map[(_X % _Size) + sizex2 * (_Y % _Size)];
00643 float *curr = up;
00644 const float *endUp = up + _Size;
00645 const uint downOff = (_Size - 1) * sizex2;
00646 do
00647 {
00648 *curr = curr[downOff] = 0.f;
00649 ++curr;
00650 }
00651 while (curr != endUp);
00652
00653
00654 curr = up;
00655 const float *endLeft = up + downOff;
00656 const uint rightOff = _Size - 1;
00657 do
00658 {
00659 *curr = curr[rightOff] = 0.f;
00660 curr += sizex2;
00661 }
00662 while (curr != endLeft);
00663 }
00664
00665
00666
00667 void CWaterHeightMap::setWaves(float intensity, float period, uint radius, bool border)
00668 {
00669 _WaveIntensity = intensity;
00670 _WavePeriod = period;
00671 _WaveImpulsionRadius = radius;
00672 _BorderWaves = border;
00673
00674 }
00675
00676
00677
00678
00679 void CWaterHeightMap::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
00680 {
00681 f.xmlPushBegin("WaterHeightMap");
00682 f.xmlSetAttrib ("NAME") ;
00683 f.serial (_Name);
00684 f.xmlPushEnd();
00685 (void)f.serialVersion(0);
00686 f.xmlSerial(_Size, "SIZE");
00687 if (f.isReading())
00688 {
00689 setSize(_Size);
00690 }
00691 f.xmlSerial(_Damping, "DAMPING");
00692 f.xmlSerial(_FilterWeight, "FILTER_WEIGHT");
00693 f.xmlSerial(_UnitSize, "WATER_UNIT_SIZE");
00694 f.xmlSerial(_WavesEnabled, "WavesEnabled");
00695 if (_WavesEnabled)
00696 {
00697 f.xmlPush("WavesParams");
00698 f.xmlSerial(_WaveIntensity, "WAVE_INTENSITY");
00699 f.xmlSerial(_WavePeriod, "WAVE_PERIOD");
00700 f.xmlSerial(_WaveImpulsionRadius, "WAVE_IMPULSION_RADIUS");
00701 f.xmlSerial(_BorderWaves, "BORDER_WAVES");
00702 f.xmlSerial(_PropagationTime, "PROPAGATION_TIME");
00703 f.xmlPop();
00704 }
00705 f.xmlPop();
00706 }
00707
00708
00709
00710
00711
00712
00713
00714
00715 static float inline BilinFilter(float v0, float v1, float v2, float v3, float u, float v)
00716 {
00717 const float g = v * v3 + (1.f - v) * v0;
00718 const float h = v * v2 + (1.f - v) * v1;
00719 return u * h + (1.f - u) * g;
00720 }
00721
00722
00723
00724
00725
00726 float CWaterHeightMap::getHeight(const NLMISC::CVector2f &pos)
00727 {
00728 const float invUnitSize = 1.f / _UnitSize;
00729
00730 const float xPos = invUnitSize * pos.x;
00731 const float yPos = invUnitSize * pos.y;
00732
00733
00734 if ((uint) xPos - _X < _Size - 1
00735 && (uint) yPos - _Y < _Size - 1
00736 )
00737
00738 {
00739
00740 const sint orgX = _X - _X % _Size;
00741 const sint orgY = _Y - _Y % _Size;
00742 const uint sizeX2 = _Size << 1;
00743
00744
00745 const sint fxPos = (sint) floorf(xPos);
00746 const sint fyPos = (sint) floorf(yPos);
00747
00748
00749
00750 const float deltaU = xPos - fxPos;
00751 const float deltaV = yPos - fyPos;
00752 const uint offset = (uint) fxPos - orgX + sizeX2 * ( (uint) fyPos - orgY);
00753 const float lambda = getBufferRatio();
00754 const float *map1 = getPrevPointer();
00755 const float *map2 = getPointer();
00756
00757 return BilinFilter(lambda * map2[offset] + (1.f - lambda) * map1[offset ],
00758 lambda * map2[offset + 1] + (1.f - lambda) * map1[offset + 1],
00759 lambda * map2[offset + sizeX2 + 1] + (1.f - lambda) * map1[offset + sizeX2 + 1],
00760 lambda * map2[offset + sizeX2 ] + (1.f - lambda) * map1[offset + sizeX2 ],
00761 deltaU,
00762 deltaV
00763 );
00764 }
00765 else return 0;
00766
00767 }
00768
00769 }