Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / vegetable.cpp
blobb6acf831fb612078e6f62ce4ade76c98055988f8
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/vegetable.h"
20 #include "nel/misc/common.h"
21 #include "nel/3d/vegetable_manager.h"
22 #include "nel/misc/fast_floor.h"
25 using namespace std;
26 using namespace NLMISC;
28 #ifdef DEBUG_NEW
29 #define new DEBUG_NEW
30 #endif
32 namespace NL3D
36 // ***************************************************************************
37 // Generate random value, but seed is spacial. Take a high frequency, so it gets more the aspect of random.
38 static NLMISC::CNoiseValue RandomGenerator(0,1, 7.68f);
41 // ***************************************************************************
42 CVegetable::CVegetable()
44 // Ground style density.
45 setAngleGround(0);
47 // Density not maximised.
48 MaxDensity= -1;
50 // No scale.
51 Sxy.Abs= Sz.Abs= 1;
52 Sxy.Rand= Sz.Rand= 0;
53 // No rotation.
54 Rx.Abs= Ry.Abs= Rz.Abs= 0;
55 Rx.Rand= Ry.Rand= Rz.Rand= 0;
56 // No BendFactor.
57 BendFactor.Abs= 1;
58 BendFactor.Rand= 0;
59 BendFrequencyFactor= 1;
61 // Appear at 0.
62 DistType= 0;
64 _Manager= NULL;
68 // ***************************************************************************
69 void CVegetable::setAngleGround(float cosAngleMin)
71 _AngleType= AngleGround;
73 _CosAngleMin= cosAngleMin;
74 // We must be at densityFactor==1, when cosAngle==1, keeping the same formula.
75 _CosAngleMax= 1 + (1-cosAngleMin);
77 // precalc
78 _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2;
79 _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle;
80 if(_OOCosAngleDist)
81 _OOCosAngleDist= 1.0f / _OOCosAngleDist;
84 // ***************************************************************************
85 void CVegetable::setAngleCeiling(float cosAngleMax)
87 _AngleType= AngleCeiling;
89 _CosAngleMax= cosAngleMax;
90 // We must be at densityFactor==1, when cosAngle==-1, keeping the same formula.
91 _CosAngleMin= -1 - (cosAngleMax-(-1));
93 // precalc
94 _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2;
95 _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle;
96 if(_OOCosAngleDist)
97 _OOCosAngleDist= 1.0f / _OOCosAngleDist;
100 // ***************************************************************************
101 void CVegetable::setAngleWall(float cosAngleMin, float cosAngleMax)
103 _AngleType= AngleWall;
105 _CosAngleMin= cosAngleMin;
106 _CosAngleMax= cosAngleMax;
108 // precalc
109 _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2;
110 _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle;
111 if(_OOCosAngleDist)
112 _OOCosAngleDist= 1.0f / _OOCosAngleDist;
116 // ***************************************************************************
117 void CVegetable::registerToManager(CVegetableManager *manager)
119 nlassert(manager);
120 _Manager= manager;
121 _VegetableShape= _Manager->getVegetableShape(ShapeName);
125 // ***************************************************************************
126 void CVegetable::generateGroupEx(float nbInst, const CVector &posInWorld, const CVector &surfaceNormal, uint vegetSeed, std::vector<CVector2f> &instances) const
128 nlassert(_Manager);
130 // Density modulation.
131 //===================
133 // compute cos of angle between surfaceNormal and K(0,0,1).
134 float cosAngle= surfaceNormal.z;
135 // compute angleFactor density. Use a quadratic, because f'(_CosAngleMiddle)==0.
136 float angleFact= 1 - sqr((cosAngle - _CosAngleMiddle) * _OOCosAngleDist);
137 angleFact= max(0.f, angleFact);
138 // modulate density with angleFactor.
139 nbInst*= angleFact;
141 // modulate result by Global Manager density
142 nbInst*= _Manager->getGlobalDensity();
144 // Now, 0<=nbInst<+oo. If we have 0.1, it means that we have 10% chance to spawn an instance.
145 // So add a "random" value (with help of a noise with High frequency)
146 // if nbInst==0, we should never have any instance (which may arise if evalOneLevelRandom()==1).
147 // hence the 0.99f* which ensure that we do nbInst+= [0..1[.
148 nbInst+= 0.99f * RandomGenerator.evalOneLevelRandom(posInWorld);
150 // and then get only the integral part.
151 sint nbInstances= NLMISC::OptFastFloor(nbInst);
152 nbInstances= max(0, nbInstances);
154 // resize the instances
155 instances.resize(nbInstances);
157 // Position generation.
158 //===================
159 // For now, generate them randomly.
160 static CVector2f dSeed(0.513f, 0.267f); // random values.
161 CVector seed= posInWorld;
162 seed.z+= vegetSeed * 0.723f; // 0.723f is a random value.
163 for(sint i=0; i<nbInstances; i++)
165 instances[i].x= RandomGenerator.evalOneLevelRandom(seed);
166 seed.x+= dSeed.x;
167 instances[i].y= RandomGenerator.evalOneLevelRandom(seed);
168 seed.y+= dSeed.y;
173 // ***************************************************************************
174 void CVegetable::generateGroup(const CVector &posInWorld, const CVector &surfaceNormal, float area, uint vegetSeed, std::vector<CVector2f> &instances) const
176 // number of instances to generate
177 float dens= Density.eval(posInWorld);
178 if(MaxDensity >= 0)
179 dens= min(dens, MaxDensity);
180 float nbInst= area * dens;
182 // modulate by normal and generate them.
183 generateGroupEx(nbInst, posInWorld, surfaceNormal, vegetSeed, instances);
187 // ***************************************************************************
188 void CVegetable::generateGroupBiLinear(const CVector &posInWorld, const CVector posInWorldBorder[4], const CVector &surfaceNormal, float area, uint vegetSeed, std::vector<CVector2f> &instances) const
190 sint i;
191 const float evenDistribFact= 12.25f; // an arbitrary value to have a higher frequency for random.
193 // compute how many instances to generate on borders of the patch
194 // ==================
195 float edgeDensity[4];
196 for(i=0; i<4; i++)
198 // Get number of instances generated on edges
199 edgeDensity[i]= area * Density.eval(posInWorldBorder[i]);
200 if(MaxDensity >= 0)
201 edgeDensity[i]= min(edgeDensity[i], area * MaxDensity);
202 edgeDensity[i]= max(0.f, edgeDensity[i]);
204 // Average on center of the patch for each direction.
205 float edgeDensityCenterX;
206 float edgeDensityCenterY;
207 edgeDensityCenterX= 0.5f * (edgeDensity[0] + edgeDensity[1]);
208 edgeDensityCenterY= 0.5f * (edgeDensity[2] + edgeDensity[3]);
211 // Average for all the patch
212 float nbInstAverage= 0.5f * (edgeDensityCenterX + edgeDensityCenterY);
215 // generate instances on the patch
216 // ==================
217 generateGroupEx(nbInstAverage, posInWorld, surfaceNormal, vegetSeed, instances);
221 // move instances x/y to follow edge repartition
222 // ==================
223 // If on a direction, both edges are 0 density, then must do a special formula
224 bool middleX= edgeDensityCenterX<=1;
225 bool middleY= edgeDensityCenterY<=1;
226 float OOEdgeDCX=0.0;
227 float OOEdgeDCY=0.0;
228 if(!middleX) OOEdgeDCX= 1.0f / edgeDensityCenterX;
229 if(!middleY) OOEdgeDCY= 1.0f / edgeDensityCenterY;
230 // for all instances
231 for(i=0; i<(sint)instances.size(); i++)
233 float x= instances[i].x;
234 float y= instances[i].y;
235 // a seed for random.
236 CVector randSeed(x*evenDistribFact, y*evenDistribFact, 0);
238 // X change.
239 if(middleX)
241 // instances are grouped at middle. this is the bijection of easeInEaseOut
242 x= x+x - easeInEaseOut(x);
243 x= x+x - easeInEaseOut(x);
244 instances[i].x= x;
246 else
248 // Swap X, randomly. swap more on border
249 // evaluate the density in X direction we have at this point.
250 float densX= edgeDensity[0]*(1-x) + edgeDensity[1]* x ;
251 // If on the side of the lowest density
252 if(densX < edgeDensityCenterX)
254 // may swap the position
255 float rdSwap= (densX * OOEdgeDCX );
256 // (densX * OOEdgeDCX) E [0..1[. The more it is near 0, the more is has chance to be swapped.
257 rdSwap+= RandomGenerator.evalOneLevelRandom( randSeed );
258 if(rdSwap<1)
259 instances[i].x= 1 - instances[i].x;
263 // Y change.
264 if(middleY)
266 // instances are grouped at middle. this is the bijection of easeInEaseOut
267 y= y+y - easeInEaseOut(y);
268 y= y+y - easeInEaseOut(y);
269 instances[i].y= y;
271 else
273 // Swap Y, randomly. swap more on border
274 // evaluate the density in Y direction we have at this point.
275 float densY= edgeDensity[2]*(1-y) + edgeDensity[3]* y ;
276 // If on the side of the lowest density
277 if(densY < edgeDensityCenterY)
279 // may swap the position
280 float rdSwap= (densY * OOEdgeDCY);
281 // (densY * OOEdgeDCY) E [0..1[. The more it is near 0, the more is has chance to be swapped.
282 rdSwap+= RandomGenerator.evalOneLevelRandom( randSeed );
283 if(rdSwap<1)
284 instances[i].y= 1 - instances[i].y;
293 // ***************************************************************************
294 void CVegetable::reserveIgAddInstances(CVegetableInstanceGroupReserve &vegetIgReserve, TVegetableWater vegetWaterState, uint numInstances) const
296 nlassert(_Manager);
298 if (_VegetableShape)
299 _Manager->reserveIgAddInstances(vegetIgReserve, _VegetableShape, (CVegetableManager::TVegetableWater)vegetWaterState, numInstances);
303 // ***************************************************************************
304 void CVegetable::generateInstance(CVegetableInstanceGroup *ig, const NLMISC::CMatrix &posInWorld,
305 const NLMISC::CRGBAF &modulateAmbientColor, const NLMISC::CRGBAF &modulateDiffuseColor, float blendDistMax,
306 TVegetableWater vegetWaterState, CVegetableUV8 dlmUV) const
308 nlassert(_Manager);
311 CVector seed= posInWorld.getPos();
313 // Generate Matrix.
314 // ===============
316 // Generate a random Scale / Rotation matrix.
317 CMatrix randomMat;
318 // setup rotation
319 CVector rot;
320 rot.x= Rx.eval(seed);
321 rot.y= Ry.eval(seed);
322 rot.z= Rz.eval(seed);
323 randomMat.setRot(rot, CMatrix::ZXY);
324 // scale.
325 if(Sxy.Abs!=0 || Sxy.Rand!=0 || Sz.Abs!=0 || Sz.Rand!=0)
327 CVector scale;
328 scale.x= scale.y= Sxy.eval(seed);
329 scale.z= Sz.eval(seed);
330 randomMat.scale(scale);
333 // Final Matrix.
334 CMatrix finalMatrix;
335 finalMatrix= posInWorld * randomMat;
337 // Generate Color and factor
338 // ===============
339 CRGBAF materialColor(1,1,1,1);
340 // evaluate gradients. If none, color not modified.
341 Color.eval(seed, materialColor);
342 // modulate with user
343 CRGBAF ambient, diffuse;
344 if(_VegetableShape && _VegetableShape->Lighted)
346 ambient= modulateAmbientColor * materialColor;
347 diffuse= modulateDiffuseColor * materialColor;
349 else
351 ambient= materialColor;
352 diffuse= materialColor;
355 // Generate a bendFactor
356 float bendFactor= BendFactor.eval(seed);
357 // Generate a bendPhase
358 float bendPhase= BendPhase.eval(seed);
361 // Append to the vegetableManager
362 // ===============
363 if (_VegetableShape)
365 _Manager->addInstance(ig, _VegetableShape, finalMatrix, ambient, diffuse,
366 bendFactor, bendPhase, BendFrequencyFactor, blendDistMax,
367 (CVegetableManager::TVegetableWater)vegetWaterState, dlmUV);
372 // ***************************************************************************
373 void CVegetable::serial(NLMISC::IStream &f)
376 Version 1:
377 - add BendFrequencyFactor
378 Version 0:
379 - base version
381 sint ver= f.serialVersion(1);
383 f.serial(ShapeName);
384 f.serial(Density);
385 f.serial(MaxDensity);
386 f.serial(_CosAngleMin, _CosAngleMax, _CosAngleMiddle, _OOCosAngleDist);
387 f.serialEnum(_AngleType);
388 f.serial(Sxy, Sz);
389 f.serial(Rx, Ry, Rz);
390 f.serial(BendFactor);
391 f.serial(BendPhase);
392 f.serial(Color);
393 f.serial(DistType);
395 if(ver>=1)
396 f.serial(BendFrequencyFactor);
397 else
398 BendFrequencyFactor= 1;
402 } // NL3D