Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / noise_value.cpp
blob28fe24001ccf53060943942fef025cf066e5be8b
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 "stdmisc.h"
19 #include "nel/misc/noise_value.h"
20 #include "nel/misc/fast_floor.h"
23 #ifdef DEBUG_NEW
24 #define new DEBUG_NEW
25 #endif
27 namespace NLMISC
31 // 3 level: best quality/speed ratio.
32 #define NL3D_NOISE_LEVEL 3
33 #define NL3D_NOISE_GRID_SIZE_SHIFT 5
34 #define NL3D_NOISE_GRID_SIZE (1<<NL3D_NOISE_GRID_SIZE_SHIFT)
35 static const float NL3D_OO255= 1.0f / 255;
37 // ***************************************************************************
38 // ***************************************************************************
39 // ***************************************************************************
41 // ***************************************************************************
42 /// A static 3D array of random value + other infos for noise
43 class CRandomGrid3D
45 public:
47 // generate a random grid, with same seed.
48 CRandomGrid3D()
50 //seed
51 srand(0);
53 // init the grid
54 for(uint z=0; z<NL3D_NOISE_GRID_SIZE; z++)
56 for(uint y=0; y<NL3D_NOISE_GRID_SIZE; y++)
58 for(uint x=0; x<NL3D_NOISE_GRID_SIZE; x++)
60 uint id= x + (y<<NL3D_NOISE_GRID_SIZE_SHIFT) + (z<<(NL3D_NOISE_GRID_SIZE_SHIFT*2));
61 // take higher bits of rand gives better result.
62 uint v= rand() >> 5;
63 _Texture3d[id]= v&255;
68 // init sizes.
69 uint i;
70 // sum of sizes must be 1, and each level must be /2.
71 float sizeSum=0;
72 for(i=0; i<NL3D_NOISE_LEVEL; i++)
74 _Sizes[i]= 1.0f / (1<<i);
75 sizeSum+= _Sizes[i];
77 // normalize
78 for(i=0; i<NL3D_NOISE_LEVEL; i++)
80 _Sizes[i]/= sizeSum;
83 // init LevelPhases.
84 for(i=0; i<NL3D_NOISE_LEVEL; i++)
86 _LevelPhase[i].x= frand(NL3D_NOISE_GRID_SIZE);
87 _LevelPhase[i].y= frand(NL3D_NOISE_GRID_SIZE);
88 _LevelPhase[i].z= frand(NL3D_NOISE_GRID_SIZE);
90 // not for level 0.
91 _LevelPhase[0]= CVector::Null;
94 // x/y/z are use to lookup directly in the grid 3D.
95 static inline float evalNearest(const CVector &pos)
97 // compute integer part.
98 sint x= OptFastFloor(pos.x);
99 sint y= OptFastFloor(pos.y);
100 sint z= OptFastFloor(pos.z);
101 // index in texture.
102 uint ux= x& (NL3D_NOISE_GRID_SIZE-1);
103 uint uy= y& (NL3D_NOISE_GRID_SIZE-1);
104 uint uz= z& (NL3D_NOISE_GRID_SIZE-1);
106 // read the texture.
107 float turb= lookup(ux,uy,uz);
109 return turb*NL3D_OO255;
112 // x/y/z are use to lookup directly in the grid 3D.
113 static inline float evalBiLinear(const CVector &pos)
115 // compute integer part.
116 sint x= OptFastFloor(pos.x);
117 sint y= OptFastFloor(pos.y);
118 sint z= OptFastFloor(pos.z);
119 // index in texture.
120 uint ux= x& (NL3D_NOISE_GRID_SIZE-1);
121 uint uy= y& (NL3D_NOISE_GRID_SIZE-1);
122 uint uz= z& (NL3D_NOISE_GRID_SIZE-1);
123 uint ux2= (x+1)& (NL3D_NOISE_GRID_SIZE-1);
124 uint uy2= (y+1)& (NL3D_NOISE_GRID_SIZE-1);
125 uint uz2= (z+1)& (NL3D_NOISE_GRID_SIZE-1);
126 // delta.
127 float dx2;
128 float dy2;
129 float dz2;
130 easeInEaseOut(dx2, pos.x-x);
131 easeInEaseOut(dy2, pos.y-y);
132 easeInEaseOut(dz2, pos.z-z);
133 float dx= 1-dx2;
134 float dy= 1-dy2;
135 float dz= 1-dz2;
136 // TriLinear in texture3D.
137 float turb=0;
138 float dxdy= dx*dy;
139 turb+= lookup(ux,uy,uz)* dxdy*dz;
140 turb+= lookup(ux,uy,uz2)* dxdy*dz2;
141 float dxdy2= dx*dy2;
142 turb+= lookup(ux,uy2,uz)* dxdy2*dz;
143 turb+= lookup(ux,uy2,uz2)* dxdy2*dz2;
144 float dx2dy= dx2*dy;
145 turb+= lookup(ux2,uy,uz)* dx2dy*dz;
146 turb+= lookup(ux2,uy,uz2)* dx2dy*dz2;
147 float dx2dy2= dx2*dy2;
148 turb+= lookup(ux2,uy2,uz)* dx2dy2*dz;
149 turb+= lookup(ux2,uy2,uz2)* dx2dy2*dz2;
151 // End!
152 return turb*NL3D_OO255;
156 // get size according to level
157 static inline float getLevelSize(uint level)
159 return _Sizes[level];
162 // get an additional level phase.
163 static inline const CVector &getLevelPhase(uint level)
165 return _LevelPhase[level];
169 // **************
170 private:
172 static uint8 _Texture3d[NL3D_NOISE_GRID_SIZE*NL3D_NOISE_GRID_SIZE*NL3D_NOISE_GRID_SIZE];
173 static float _Sizes[NL3D_NOISE_LEVEL];
174 static CVector _LevelPhase[NL3D_NOISE_LEVEL];
177 // lookup with no mod.
178 static inline float lookup(uint ux, uint uy, uint uz)
180 uint id= ux + (uy<<NL3D_NOISE_GRID_SIZE_SHIFT) + (uz<<(NL3D_NOISE_GRID_SIZE_SHIFT*2));
181 return _Texture3d[id];
184 // easineasout
185 static inline void easeInEaseOut(float &y, float x)
187 // cubic such that f(0)=0, f'(0)=0, f(1)=1, f'(1)=0.
188 float x2=x*x;
189 float x3=x2*x;
190 y= -2*x3 + 3*x2;
196 uint8 CRandomGrid3D::_Texture3d[NL3D_NOISE_GRID_SIZE*NL3D_NOISE_GRID_SIZE*NL3D_NOISE_GRID_SIZE];
197 float CRandomGrid3D::_Sizes[NL3D_NOISE_LEVEL];
198 CVector CRandomGrid3D::_LevelPhase[NL3D_NOISE_LEVEL];
200 // just to init the static arrays.
201 static CRandomGrid3D NL3D_RandomGrid3D;
204 // ***************************************************************************
205 // ***************************************************************************
206 // ***************************************************************************
209 // ***************************************************************************
210 float CNoiseValue::evalRandom(const CVector &pos) const
212 return CRandomGrid3D::evalNearest(pos);
216 // ***************************************************************************
217 float CNoiseValue::noise(const CVector &pos) const
219 // eval "fractaly".
220 float turb;
222 #if (NL3D_NOISE_LEVEL != 3)
223 CVector vd= pos;
224 turb=0;
225 for(uint level=0;level<NL3D_NOISE_LEVEL;level++)
227 // Add the influence of the ith level.
228 turb+= CRandomGrid3D::getLevelSize(level) *
229 CRandomGrid3D::evalBiLinear(vd + CRandomGrid3D::getLevelPhase(level) );
230 // Next level at higher frequency
231 vd*= 2;
233 #else
234 // special case. unrolled loop.
235 // level 0 has no phase.
236 turb= CRandomGrid3D::getLevelSize(0) *
237 CRandomGrid3D::evalBiLinear(pos);
238 // level 1
239 turb+= CRandomGrid3D::getLevelSize(1) *
240 CRandomGrid3D::evalBiLinear(pos*2 + CRandomGrid3D::getLevelPhase(1) );
241 // level 2
242 turb+= CRandomGrid3D::getLevelSize(2) *
243 CRandomGrid3D::evalBiLinear(pos*4 + CRandomGrid3D::getLevelPhase(2) );
244 #endif
246 return turb;
251 // ***************************************************************************
252 CNoiseValue::CNoiseValue()
254 Abs= 0;
255 Rand= 1;
256 Frequency= 1;
260 // ***************************************************************************
261 CNoiseValue::CNoiseValue(float abs, float rand, float freq)
263 Abs= abs;
264 Rand= rand;
265 Frequency= freq;
269 // ***************************************************************************
270 float CNoiseValue::eval(const CVector &posInWorld) const
272 // A single cube in the Grid3d correspond to Frequency==1.
273 // So enlarging size of the grid3d do not affect the frequency aspect.
274 return Abs + Rand * noise(posInWorld*Frequency);
278 // ***************************************************************************
279 float CNoiseValue::evalOneLevelRandom(const CVector &posInWorld) const
281 // A single cube in the Grid3d correspond to Frequency==1.
282 // So enlarging size of the grid3d do not affect the frequency aspect.
283 return Abs + Rand * evalRandom(posInWorld*Frequency);
287 // ***************************************************************************
288 void CNoiseValue::serial(IStream &f)
290 (void)f.serialVersion(0);
291 f.serial(Abs);
292 f.serial(Rand);
293 f.serial(Frequency);
297 // ***************************************************************************
298 // ***************************************************************************
299 // ***************************************************************************
302 // ***************************************************************************
303 void CNoiseColorGradient::eval(const CVector &posInWorld, CRGBAF &result) const
305 // test if not null grads.
306 uint nGrads= (uint)Gradients.size();
307 if(nGrads==0)
308 return;
309 // if only one color, easy
310 if(nGrads==1)
312 result= Gradients[0];
314 else
316 // eval noise
317 float f= NoiseValue.eval(posInWorld) * (nGrads-1);
318 clamp(f, 0.f, (float)(nGrads-1));
319 // look up in table of gradients.
320 uint id= OptFastFloor(f);
321 clamp(id, 0U, nGrads-2);
322 // fractionnal part.
323 f= f-id;
324 clamp(f, 0, 1);
325 // interpolate the gradient.
326 result= Gradients[id]*(1-f) + Gradients[id+1]*f;
330 // ***************************************************************************
331 void CNoiseColorGradient::serial(IStream &f)
333 (void)f.serialVersion(0);
334 f.serial(NoiseValue);
335 f.serialCont(Gradients);
340 } // NLMISC