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/vegetable.h"
20 #include "nel/misc/common.h"
21 #include "nel/3d/vegetable_manager.h"
22 #include "nel/misc/fast_floor.h"
26 using namespace NLMISC
;
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.
47 // Density not maximised.
54 Rx
.Abs
= Ry
.Abs
= Rz
.Abs
= 0;
55 Rx
.Rand
= Ry
.Rand
= Rz
.Rand
= 0;
59 BendFrequencyFactor
= 1;
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
);
78 _CosAngleMiddle
= (_CosAngleMin
+ _CosAngleMax
)/2;
79 _OOCosAngleDist
= _CosAngleMax
- _CosAngleMiddle
;
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));
94 _CosAngleMiddle
= (_CosAngleMin
+ _CosAngleMax
)/2;
95 _OOCosAngleDist
= _CosAngleMax
- _CosAngleMiddle
;
97 _OOCosAngleDist
= 1.0f
/ _OOCosAngleDist
;
100 // ***************************************************************************
101 void CVegetable::setAngleWall(float cosAngleMin
, float cosAngleMax
)
103 _AngleType
= AngleWall
;
105 _CosAngleMin
= cosAngleMin
;
106 _CosAngleMax
= cosAngleMax
;
109 _CosAngleMiddle
= (_CosAngleMin
+ _CosAngleMax
)/2;
110 _OOCosAngleDist
= _CosAngleMax
- _CosAngleMiddle
;
112 _OOCosAngleDist
= 1.0f
/ _OOCosAngleDist
;
116 // ***************************************************************************
117 void CVegetable::registerToManager(CVegetableManager
*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
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.
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
);
167 instances
[i
].y
= RandomGenerator
.evalOneLevelRandom(seed
);
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
);
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
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];
198 // Get number of instances generated on edges
199 edgeDensity
[i
]= area
* Density
.eval(posInWorldBorder
[i
]);
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;
228 if(!middleX
) OOEdgeDCX
= 1.0f
/ edgeDensityCenterX
;
229 if(!middleY
) OOEdgeDCY
= 1.0f
/ edgeDensityCenterY
;
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);
241 // instances are grouped at middle. this is the bijection of easeInEaseOut
242 x
= x
+x
- easeInEaseOut(x
);
243 x
= x
+x
- easeInEaseOut(x
);
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
);
259 instances
[i
].x
= 1 - instances
[i
].x
;
266 // instances are grouped at middle. this is the bijection of easeInEaseOut
267 y
= y
+y
- easeInEaseOut(y
);
268 y
= y
+y
- easeInEaseOut(y
);
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
);
284 instances
[i
].y
= 1 - instances
[i
].y
;
293 // ***************************************************************************
294 void CVegetable::reserveIgAddInstances(CVegetableInstanceGroupReserve
&vegetIgReserve
, TVegetableWater vegetWaterState
, uint numInstances
) const
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
311 CVector seed
= posInWorld
.getPos();
316 // Generate a random Scale / Rotation matrix.
320 rot
.x
= Rx
.eval(seed
);
321 rot
.y
= Ry
.eval(seed
);
322 rot
.z
= Rz
.eval(seed
);
323 randomMat
.setRot(rot
, CMatrix::ZXY
);
325 if(Sxy
.Abs
!=0 || Sxy
.Rand
!=0 || Sz
.Abs
!=0 || Sz
.Rand
!=0)
328 scale
.x
= scale
.y
= Sxy
.eval(seed
);
329 scale
.z
= Sz
.eval(seed
);
330 randomMat
.scale(scale
);
335 finalMatrix
= posInWorld
* randomMat
;
337 // Generate Color and factor
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
;
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
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
)
377 - add BendFrequencyFactor
381 sint ver
= f
.serialVersion(1);
385 f
.serial(MaxDensity
);
386 f
.serial(_CosAngleMin
, _CosAngleMax
, _CosAngleMiddle
, _OOCosAngleDist
);
387 f
.serialEnum(_AngleType
);
389 f
.serial(Rx
, Ry
, Rz
);
390 f
.serial(BendFactor
);
396 f
.serial(BendFrequencyFactor
);
398 BendFrequencyFactor
= 1;