1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
34 //===========================================================================================
36 CWaterHeightMap::CWaterHeightMap() : Date(-1),
43 _WaveImpulsionRadius(3),
46 _PropagateEllapsedTime(0),
47 _PropagationTime(0.10f
),
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()
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?
84 if ((sint
) _X
>= 0) XDivSize
= (sint
) _X
/ (sint
) _Size
;
85 else XDivSize
= ((sint
) (_X
+ 1) / (sint
) _Size
) - 1;
88 if ((sint
) _Y
>= 0) YDivSize
= (sint
) _Y
/ (sint
) _Size
;
89 else YDivSize
= ((sint
) (_Y
+ 1) / (sint
) _Size
) - 1;
92 if (x
>= 0) xDivSize
= (sint
) x
/ (sint
) _Size
;
93 else xDivSize
= ((sint
) (x
+ 1) / (sint
) _Size
) - 1;
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
;
116 if (yDivSize
!= YDivSize
)
118 offsetY
= yDivSize
< YDivSize
? _Size
: -(sint
)_Size
;
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
;
142 // x, y, width, height
143 clearZone(x
- newOrgX
, y
- newOrgY
, _Size
, _Y
- y
);
147 clearZone(x
- newOrgX
, y
+ _Size
- newOrgY
, _Size
, y
- _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
);
165 clearZone(_X
+ _Size
- newOrgX
, y
- newOrgY
, x
- _X
, _Size
- (y
- _Y
));
166 clearZone(x
- newOrgX
, _Y
+ _Size
- newOrgY
, _Size
, y
- _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
));
182 clearZone(x
- newOrgX
, y
- newOrgY
, _X
- x
, _Size
- (y
-_Y
));
183 clearZone(x
- newOrgX
, _Y
+ _Size
- newOrgY
, _Size
, y
- _Y
);
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
);
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
));
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
)));
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
);
237 _PropagateEllapsedTime
= 0;
241 const float endTime
= _PropagateEllapsedTime
+ deltaT
;
242 const float startTime
= _PropagateEllapsedTime
;
244 if (endTime
< _PropagationTime
)
246 animatePart(startTime
, endTime
);
247 _PropagateEllapsedTime
= endTime
;
251 animatePart(startTime
, _PropagationTime
);
253 //animatePart(0, endTime - _PropagationTime);
255 _PropagateEllapsedTime
= 0 /*endTime - _PropagationTime*/;
258 animateWaves(deltaT
);
261 //===========================================================================================
263 void CWaterHeightMap::setSize(uint32 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
);
292 const sint stride
= _Size
<< 1;
296 src
= &_Map
[buffer
][sX
+ sY
* stride
];
297 dest
= &_Map
[buffer
][dX
+ dY
* stride
];
302 src
= &_Map
[buffer
][sX
+ (sY
+ height
- 1) * stride
];
303 dest
= &_Map
[buffer
][dX
+ (dY
+ height
- 1) * stride
];
312 std::copy(src
, src
+ width
, stdext::make_unchecked_array_iterator(dest
));
314 std::copy(src
, src
+ width
, dest
);
319 float *rSrc
= src
+ width
;
320 float *rDest
= dest
+ width
;
335 //===========================================================================================
337 void CWaterHeightMap::setUserPos(sint x
, sint 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
;
361 nlassert(_Size
!= 0);
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;
379 *dest
= damping
* ( 0.5f
* (buf1
[1] + buf1
[-1] + buf1
[sizeX2
] + buf1
[- sizeX2
]) - *buf2
);
385 buf1
= buf1
+ _Size
+ 2;
386 buf2
= buf2
+ _Size
+ 2;
387 dest
= dest
+ _Size
+ 2;
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);
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];
411 const float totalBlurCoeff
= (1.f
/ (4.f
+ blurCoeff
));
412 const sint sizeX2
= _Size
<< 1;
418 *buf
= totalBlurCoeff
* (*buf
* blurCoeff
425 /*ptGrad->x = buf[1] - buf[- 1];
426 ptGrad->y = buf[sizeX2] - buf[- sizeX2];*/
433 //ptGrad += _Size + 2;
438 //===========================================================================================
440 void CWaterHeightMap::animateWaves(float deltaT
)
445 if (_WavePeriod
== 0)
451 _EmitEllapsedTime
+= deltaT
;
452 if (_EmitEllapsedTime
> _WavePeriod
)
454 numWaves
= (uint
) (_EmitEllapsedTime
/ _WavePeriod
);
455 _EmitEllapsedTime
-= numWaves
* _WavePeriod
;
456 if (numWaves
> 10) numWaves
= 10;
465 // generate automatic waves
468 if (_WaveIntensity
!= 0)
470 for (k
= 0; k
< numWaves
; ++k
)
472 perturbate(_NewX
+ rand() % _Size
, _NewY
+ rand() % _Size
, _WaveImpulsionRadius
, _WaveIntensity
);
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
);
486 case 1: // bottom border
487 for (k
= 0; k
< numWaves
; ++k
)
489 perturbate(_NewX
+ (uint
) rand() % _Size
, _NewY
+ _Size
- 1, _WaveImpulsionRadius
, _WaveIntensity
);
492 case 2: // right border
493 for (k
= 0; k
< numWaves
; ++k
)
495 perturbate(_NewX
+ _Size
- 1, _NewY
+ (uint
) rand() % _Size
, _WaveImpulsionRadius
, _WaveIntensity
);
498 case 3: // left border
499 for (k
= 0; k
< numWaves
; ++k
)
501 perturbate(_NewX
, _NewY
+ (uint
) rand() % _Size
, _WaveImpulsionRadius
, _WaveIntensity
);
510 //===========================================================================================
512 void CWaterHeightMap::swapBuffers(float deltaT
)
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
)
534 nlassert(width
>= 0);
535 nlassert(height
>= 0);
536 uint sizex2
= _Size
<< 1;
542 if (width
<= 0) return;
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);
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
);
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;
640 float *up
= &map
[(_X
% _Size
) + sizex2
* (_Y
% _Size
)];
642 const float *endUp
= up
+ _Size
;
643 const uint downOff
= (_Size
- 1) * sizex2
;
646 *curr
= curr
[downOff
] = 0.f
;
649 while (curr
!= endUp
);
653 const float *endLeft
= up
+ downOff
;
654 const uint rightOff
= _Size
- 1;
657 *curr
= curr
[rightOff
] = 0.f
;
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");
683 (void)f
.serialVersion(0);
684 f
.xmlSerial(_Size
, "SIZE");
689 f
.xmlSerial(_Damping
, "DAMPING");
690 f
.xmlSerial(_FilterWeight
, "FILTER_WEIGHT");
691 f
.xmlSerial(_UnitSize
, "WATER_UNIT_SIZE");
692 f
.xmlSerial(_WavesEnabled
, "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");
709 // *** perform a bilinear on 4 values
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