Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / water_height_map.cpp
blob6fe88a8bc71c62cd28582da30c47341d19e2e196
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/3d/water_height_map.h"
20 #include "nel/misc/common.h"
21 #include "nel/misc/debug.h"
22 #include "nel/misc/vector_2f.h"
23 #include <algorithm>
24 #include <cmath>
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NL3D
34 //===========================================================================================
36 CWaterHeightMap::CWaterHeightMap() : Date(-1),
37 _WavesEnabled(false),
38 _Damping(0.97f),
39 _FilterWeight(4),
40 _UnitSize(0.6f),
41 _WaveIntensity(0),
42 _WavePeriod(0),
43 _WaveImpulsionRadius(3),
44 _BorderWaves(true),
45 _EmitEllapsedTime(0),
46 _PropagateEllapsedTime(0),
47 _PropagationTime(0.10f),
48 _X(0),
49 _Y(0),
50 _NewX(0),
51 _NewY(0),
52 _CurrMap(0),
53 _Size(0)
58 //===========================================================================================
60 void CWaterHeightMap::setPropagationTime(float time)
62 _PropagationTime = time;
63 _PropagateEllapsedTime = 0;
64 for (uint k = 0; k < NumWaterMap; ++k)
66 clearArea(k, 0, 0, _Size << 1, _Size << 1);
70 //===========================================================================================
72 void CWaterHeightMap::updateUserPos()
74 const sint x = _NewX;
75 const sint y = _NewY;
77 nlassert(_Size != 0);
78 if ((uint) x == _X && (uint) y == _Y) return;
79 if ((uint) std::abs((sint)(x - _X)) < _Size && (uint) std::abs((sint)(y - _Y)) < _Size) // are there common pixels with the previous location?
81 // compute zone
83 sint XDivSize;
84 if ((sint) _X >= 0) XDivSize = (sint) _X / (sint) _Size;
85 else XDivSize = ((sint) (_X + 1) / (sint) _Size) - 1;
87 sint YDivSize;
88 if ((sint) _Y >= 0) YDivSize = (sint) _Y / (sint) _Size;
89 else YDivSize = ((sint) (_Y + 1) / (sint) _Size) - 1;
91 sint xDivSize;
92 if (x >= 0) xDivSize = (sint) x / (sint) _Size;
93 else xDivSize = ((sint) (x + 1) / (sint) _Size) - 1;
95 sint yDivSize;
96 if (y >= 0) yDivSize = (sint) y / (sint) _Size;
97 else yDivSize = ((sint) (y + 1) / (sint) _Size) - 1;
99 // different zone -> must decal datas
100 if (xDivSize != XDivSize || yDivSize != YDivSize)
102 sint left = std::max(x, (sint) _X);
103 sint top = std::max(y, (sint) _Y);
104 sint right = std::min(x + (sint) _Size, (sint) (_X + _Size));
105 sint bottom = std::min(y + (sint) _Size, (sint) (_Y + _Size));
106 sint offsetX, offsetY;
107 if (xDivSize != XDivSize)
109 offsetX = xDivSize < XDivSize ? _Size : -(sint)_Size;
111 else
113 offsetX = 0;
116 if (yDivSize != YDivSize)
118 offsetY = yDivSize < YDivSize ? _Size : -(sint)_Size;
120 else
122 offsetY = 0;
125 sint orgX = _Size * XDivSize;
126 sint orgY = _Size * YDivSize;
127 for (uint k = 0; k < NumWaterMap; ++k)
129 makeCpy(k, (uint) (left - orgX + offsetX) , (uint) (top - orgY + offsetY),
130 (uint) (left - orgX), (uint) (top - orgY),
131 (uint) (right - left), (uint) (bottom - top));
135 sint newOrgX = _Size * xDivSize;
136 sint newOrgY = _Size * yDivSize;
137 // clear new area
138 if (x == (sint) _X)
140 if (y < (sint) _Y)
142 // x, y, width, height
143 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
145 else
147 clearZone(x - newOrgX, y + _Size - newOrgY, _Size, y - _Y);
150 else
152 if (x > (sint) _X)
154 if (y == (sint) _Y)
156 clearZone(_X + _Size - newOrgX, y - newOrgY, x - _X, _Size);
158 else if (y < (sint) _Y)
160 clearZone(_X + _Size - newOrgX, _Y - newOrgY, x - _X, _Size - (_Y - y));
161 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
163 else
165 clearZone(_X + _Size - newOrgX, y - newOrgY, x - _X, _Size - (y - _Y));
166 clearZone(x - newOrgX, _Y + _Size - newOrgY, _Size, y - _Y);
169 else
171 if (y == (sint) _Y)
173 clearZone(x - newOrgX, y - newOrgY, _X - x, _Size);
175 else if (y < (sint) _Y)
177 clearZone(x - newOrgX, y - newOrgY, _Size, _Y - y);
178 clearZone(x - newOrgX, _Y - newOrgY, _X - x, _Size - (_Y - y));
180 else
182 clearZone(x - newOrgX, y - newOrgY, _X - x, _Size - (y -_Y));
183 clearZone(x - newOrgX, _Y + _Size - newOrgY, _Size, y - _Y);
189 else
191 // the new area has no common pixel's with the previous one
192 // clear the whole new area
193 uint px = _X % _Size;
194 uint py = _Y % _Size;
195 clearZone(px, py, _Size, _Size);
198 _X = (uint) x;
199 _Y = (uint) y;
204 //===========================================================================================
206 void CWaterHeightMap::animatePart(float startTime, float endTime)
208 if (endTime < 0.5f * _PropagationTime)
210 // perform propagation
211 propagate((uint) (_Size * 2.f * startTime / _PropagationTime), (uint) (_Size * 2.f * endTime / _PropagationTime));
213 else
215 // end propagation and start filter
216 if (startTime < 0.5f * _PropagationTime)
218 propagate((uint) (_Size * 2.f * startTime / _PropagationTime), _Size);
219 filter(0, (uint) (_Size * 2.f * (endTime / _PropagationTime - 0.5f)));
221 else
223 filter((uint) (_Size * 2.f * (startTime / _PropagationTime - 0.5f)), (uint) (_Size * 2.f * (endTime / _PropagationTime - 0.5f)));
228 //===========================================================================================
230 void CWaterHeightMap::animate(float deltaT)
232 if (deltaT < 0) deltaT = 0;
233 if (deltaT > _PropagationTime)
235 animatePart(0, _PropagationTime);
236 swapBuffers(deltaT);
237 _PropagateEllapsedTime = 0;
239 else
241 const float endTime = _PropagateEllapsedTime + deltaT;
242 const float startTime = _PropagateEllapsedTime;
244 if (endTime < _PropagationTime)
246 animatePart(startTime, endTime);
247 _PropagateEllapsedTime = endTime;
249 else
251 animatePart(startTime, _PropagationTime);
252 swapBuffers(deltaT);
253 //animatePart(0, endTime - _PropagationTime);
255 _PropagateEllapsedTime = 0 /*endTime - _PropagationTime*/;
258 animateWaves(deltaT);
261 //===========================================================================================
263 void CWaterHeightMap::setSize(uint32 size)
265 nlassert(size > 4);
266 _Size = size;
267 for (uint k = 0; k < NumWaterMap; ++k)
269 _Map[k].resize(4 * _Size * _Size);
270 clearArea(k, 0, 0, _Size << 1, _Size << 1);
272 //_Grad.resize(4 * _Size * _Size);
275 //===========================================================================================
277 void CWaterHeightMap::makeCpy(uint buffer, uint dX, uint dY, uint sX, uint sY, uint width, uint height)
279 if (width == 0 || height == 0) return;
280 nlassert(dX <= (2 * _Size));
281 nlassert(dY <= (2 * _Size));
282 nlassert(sX <= (2 * _Size));
283 nlassert(sY <= (2 * _Size));
284 nlassert(dX + width <= 2 * _Size);
285 nlassert(sX + width <= 2 * _Size);
286 nlassert(dY + height <= 2 * _Size);
287 nlassert(sY + height <= 2 * _Size);
289 sint stepY;
290 float *src, *dest;
292 const sint stride = _Size << 1;
293 if (dY <= sY)
295 stepY = stride;
296 src = &_Map[buffer][sX + sY * stride];
297 dest = &_Map[buffer][dX + dY * stride];
299 else
301 stepY = -stride;
302 src = &_Map[buffer][sX + (sY + height - 1) * stride];
303 dest = &_Map[buffer][dX + (dY + height - 1) * stride];
306 sint k = height;
309 if (dest < src)
311 #ifdef NL_COMP_VC14
312 std::copy(src, src + width, stdext::make_unchecked_array_iterator(dest));
313 #else
314 std::copy(src, src + width, dest);
315 #endif
317 else
319 float *rSrc = src + width;
320 float *rDest = dest + width;
323 --rSrc;
324 --rDest;
325 *rDest = *rSrc;
327 while (rSrc != src);
329 src += stepY;
330 dest += stepY;
332 while (--k);
335 //===========================================================================================
337 void CWaterHeightMap::setUserPos(sint x, sint y)
339 _NewX = x;
340 _NewY = y;
343 //===========================================================================================
345 void CWaterHeightMap::getUserPos(sint &x, sint &y) const
347 x = (sint) _X; y = (sint) _Y;
352 //===========================================================================================
354 void CWaterHeightMap::propagate(uint start, uint end)
356 start = std::max(1u, start);
357 end = std::min((uint) (_Size - 1), end);
358 const float damping = _Damping;
359 clearBorder(0);
360 clearBorder(1);
361 nlassert(_Size != 0);
362 sint x, y;
363 uint px = _X % _Size;
364 uint py = _Y % _Size;
365 sint offset = px + 1 + ((py + start) * (_Size << 1));
366 //nlinfo("%d, %d, %d", (_CurrMap + (NumWaterMap - 1)) % NumWaterMap, _CurrMap,
367 float *buf2 = &_Map[ (_CurrMap + (NumWaterMap - 1)) % NumWaterMap][offset];
368 float *buf1 = &_Map[_CurrMap][offset];
369 float *dest = &_Map[(_CurrMap + 1) % NumWaterMap][offset];
371 const sint sizeX2 = _Size << 1;
372 y = end - start;
373 if (y <= 0) return;
376 x = _Size - 2;
379 *dest = damping * ( 0.5f * (buf1[1] + buf1[-1] + buf1[sizeX2] + buf1[- sizeX2]) - *buf2);
380 ++buf1;
381 ++buf2;
382 ++dest;
384 while (--x);
385 buf1 = buf1 + _Size + 2;
386 buf2 = buf2 + _Size + 2;
387 dest = dest + _Size + 2;
389 while (--y);
395 //===========================================================================================
397 void CWaterHeightMap::filter(uint start, uint end)
399 start = std::max(1u, start);
400 end = std::min((uint) (_Size - 1), end);
401 const float blurCoeff = _FilterWeight;
402 nlassert(_Size != 0);
403 sint x, y;
404 uint px = _X % _Size;
405 uint py = _Y % _Size;
406 sint offset = px + 1 + ((py + start) * (_Size << 1));
407 float *buf = &_Map[ (_CurrMap + 1) % NumWaterMap ][offset];
408 //NLMISC::CVector2f *ptGrad = &_Grad[offset];
409 y = end - start;
410 if (y <= 0) return;
411 const float totalBlurCoeff = (1.f / (4.f + blurCoeff));
412 const sint sizeX2 = _Size << 1;
415 x = _Size - 2;
418 *buf = totalBlurCoeff * (*buf * blurCoeff
419 + buf[1]
420 + buf[-1]
421 + buf[sizeX2]
422 + buf[- sizeX2]
424 // compute gradient
425 /*ptGrad->x = buf[1] - buf[- 1];
426 ptGrad->y = buf[sizeX2] - buf[- sizeX2];*/
428 ++buf;
429 //++ptGrad;
431 while (--x);
432 buf += _Size + 2;
433 //ptGrad += _Size + 2;
435 while (--y);
438 //===========================================================================================
440 void CWaterHeightMap::animateWaves(float deltaT)
442 if (_WavesEnabled)
444 uint numWaves;
445 if (_WavePeriod == 0)
447 numWaves = 1;
449 else
451 _EmitEllapsedTime += deltaT;
452 if (_EmitEllapsedTime > _WavePeriod)
454 numWaves = (uint) (_EmitEllapsedTime / _WavePeriod);
455 _EmitEllapsedTime -= numWaves * _WavePeriod;
456 if (numWaves > 10) numWaves = 10;
458 else
460 numWaves = 0;
464 uint k;
465 // generate automatic waves
466 if (!_BorderWaves)
468 if (_WaveIntensity != 0)
470 for (k = 0; k < numWaves; ++k)
472 perturbate(_NewX + rand() % _Size, _NewY + rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
476 else
478 switch(rand() & 3) // choose a random border
480 case 0: // top border
481 for (k = 0; k < numWaves; ++k)
483 perturbate(_NewX + (uint) rand() % _Size, _NewY, _WaveImpulsionRadius, _WaveIntensity);
485 break;
486 case 1: // bottom border
487 for (k = 0; k < numWaves; ++k)
489 perturbate(_NewX + (uint) rand() % _Size, _NewY + _Size - 1, _WaveImpulsionRadius, _WaveIntensity);
491 break;
492 case 2: // right border
493 for (k = 0; k < numWaves; ++k)
495 perturbate(_NewX + _Size - 1, _NewY + (uint) rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
497 break;
498 case 3: // left border
499 for (k = 0; k < numWaves; ++k)
501 perturbate(_NewX, _NewY + (uint) rand() % _Size, _WaveImpulsionRadius, _WaveIntensity);
503 break;
510 //===========================================================================================
512 void CWaterHeightMap::swapBuffers(float deltaT)
514 updateUserPos();
515 _CurrMap = (_CurrMap + 1) % NumWaterMap;
519 //===========================================================================================
521 void CWaterHeightMap::clearZone(sint x, sint y, sint width, sint height)
523 for (uint k = 0; k < NumWaterMap; ++k)
525 clearArea(k, x, y, width, height);
529 //===========================================================================================
531 void CWaterHeightMap::clearArea(uint8 currMap, sint x, sint y, sint width, sint height)
533 nlassert(_Size > 1);
534 nlassert(width >= 0);
535 nlassert(height >= 0);
536 uint sizex2 = _Size << 1;
538 if (x < 0)
540 width += x;
541 x = 0;
542 if (width <= 0) return;
544 if (y < 0)
546 height += y;
547 y = 0;
548 if (height <= 0) return;
550 if (x + width > (sint) sizex2)
552 width = width - (x + width - sizex2);
555 if (y + height > (sint) sizex2)
557 height = height - (y + height - sizex2);
560 float *dest = &*(_Map[ currMap ].begin() + x + (_Size << 1) * y);
563 std::fill(dest, dest + width, 0.f);
564 dest += (_Size << 1);
566 while (-- height);
571 //===========================================================================================
573 void CWaterHeightMap::perturbate(sint x, sint y, sint radius, float intensity)
575 nlassert(_Size != 0);
576 nlassert(radius > 0);
577 sint orgX = _X - _X % _Size;
578 sint orgY = _Y - _Y % _Size;
579 TFloatVect &map = _Map[(_CurrMap + 1) % NumWaterMap];
580 const uint sizeX2 = _Size << 1;
581 for (sint px = -radius + 1; px < radius; ++px)
583 for (sint py = -radius + 1; py < radius; ++py)
585 if ((uint) (x + px - orgX) < sizeX2
586 && (uint) (y + py - orgY) < sizeX2)
589 float dist = ((float) radius - sqrtf((float) (px * px + py * py ))) / (float) radius;
590 float v = dist < radius ? intensity * cosf(dist * (float) NLMISC::Pi * 0.5f) : 0.f;
591 map[x + px - orgX + sizeX2 * (y + py - orgY)] = v;
597 //===========================================================================================
599 void CWaterHeightMap::perturbate(const NLMISC::CVector2f &pos, float strenght, float radius)
601 const float invUnitSize = 1.f / _UnitSize;
602 perturbate((sint) (pos.x * invUnitSize), (sint) (pos.y * invUnitSize), (sint) radius, strenght);
605 //===========================================================================================
607 void CWaterHeightMap::perturbatePoint(sint x, sint y, float intensity)
609 sint orgX = _X - _X % _Size;
610 sint orgY = _Y - _Y % _Size;
611 uint X = (uint) (x - orgX);
612 uint Y = (uint) (y - orgY);
613 if (X < (_Size << 1)
614 && Y < (_Size << 1)
617 const uint sizex2 = _Size << 1;
618 TFloatVect &map = _Map[(_CurrMap + 1) % NumWaterMap];
619 map[X + sizex2 * Y] = intensity;
623 //===========================================================================================
625 void CWaterHeightMap::perturbatePoint(const NLMISC::CVector2f &pos, float strenght)
627 const float invUnitSize = 1.f / _UnitSize;
628 perturbatePoint((sint) (pos.x * invUnitSize), (sint) (pos.y * invUnitSize), strenght);
631 //===========================================================================================
633 void CWaterHeightMap::clearBorder(uint currMap)
635 float *map = &_Map[currMap][0];
636 uint sizex2 = _Size << 1;
638 // top and bottom
640 float *up = &map[(_X % _Size) + sizex2 * (_Y % _Size)];
641 float *curr = up;
642 const float *endUp = up + _Size;
643 const uint downOff = (_Size - 1) * sizex2;
646 *curr = curr[downOff] = 0.f;
647 ++curr;
649 while (curr != endUp);
651 // right and left
652 curr = up;
653 const float *endLeft = up + downOff;
654 const uint rightOff = _Size - 1;
657 *curr = curr[rightOff] = 0.f;
658 curr += sizex2;
660 while (curr != endLeft);
663 //===========================================================================================
665 void CWaterHeightMap::setWaves(float intensity, float period, uint radius, bool border)
667 _WaveIntensity = intensity;
668 _WavePeriod = period;
669 _WaveImpulsionRadius = radius;
670 _BorderWaves = border;
675 //===========================================================================================
677 void CWaterHeightMap::serial(NLMISC::IStream &f)
679 f.xmlPushBegin("WaterHeightMap");
680 f.xmlSetAttrib ("NAME");
681 f.serial (_Name);
682 f.xmlPushEnd();
683 (void)f.serialVersion(0);
684 f.xmlSerial(_Size, "SIZE");
685 if (f.isReading())
687 setSize(_Size);
689 f.xmlSerial(_Damping, "DAMPING");
690 f.xmlSerial(_FilterWeight, "FILTER_WEIGHT");
691 f.xmlSerial(_UnitSize, "WATER_UNIT_SIZE");
692 f.xmlSerial(_WavesEnabled, "WavesEnabled");
693 if (_WavesEnabled)
695 f.xmlPush("WavesParams");
696 f.xmlSerial(_WaveIntensity, "WAVE_INTENSITY");
697 f.xmlSerial(_WavePeriod, "WAVE_PERIOD");
698 f.xmlSerial(_WaveImpulsionRadius, "WAVE_IMPULSION_RADIUS");
699 f.xmlSerial(_BorderWaves, "BORDER_WAVES");
700 f.xmlSerial(_PropagationTime, "PROPAGATION_TIME");
701 f.xmlPop();
703 f.xmlPop();
709 // *** perform a bilinear on 4 values
710 // 0---1
711 // | |
712 // 3---2
713 static float inline BilinFilter(float v0, float v1, float v2, float v3, float u, float v)
715 const float g = v * v3 + (1.f - v) * v0;
716 const float h = v * v2 + (1.f - v) * v1;
717 return u * h + (1.f - u) * g;
722 //===========================================================================================
724 float CWaterHeightMap::getHeight(const NLMISC::CVector2f &pos)
726 const float invUnitSize = 1.f / _UnitSize;
728 const float xPos = invUnitSize * pos.x; // position in map space
729 const float yPos = invUnitSize * pos.y; // position in map space
732 if ((uint) xPos - _X < _Size - 1
733 && (uint) yPos - _Y < _Size - 1
738 const sint orgX = _X - _X % _Size;
739 const sint orgY = _Y - _Y % _Size;
740 const uint sizeX2 = _Size << 1;
743 const sint fxPos = (sint) floorf(xPos);
744 const sint fyPos = (sint) floorf(yPos);
748 const float deltaU = xPos - fxPos;
749 const float deltaV = yPos - fyPos;
750 const uint offset = (uint) fxPos - orgX + sizeX2 * ( (uint) fyPos - orgY);
751 const float lambda = getBufferRatio();
752 const float *map1 = getPrevPointer();
753 const float *map2 = getPointer();
755 return BilinFilter(lambda * map2[offset] + (1.f - lambda) * map1[offset ], // top left
756 lambda * map2[offset + 1] + (1.f - lambda) * map1[offset + 1], // top right
757 lambda * map2[offset + sizeX2 + 1] + (1.f - lambda) * map1[offset + sizeX2 + 1], // bottom right
758 lambda * map2[offset + sizeX2 ] + (1.f - lambda) * map1[offset + sizeX2 ], // bottom left
759 deltaU,
760 deltaV
763 else return 0;
767 } // NL3D