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/>.
20 #include "nel/3d/patch.h"
21 #include "nel/3d/vegetable.h"
22 #include "nel/3d/vegetable_manager.h"
23 #include "nel/3d/landscape_vegetable_block.h"
24 #include "nel/3d/landscape.h"
25 #include "nel/misc/vector.h"
26 #include "nel/misc/common.h"
27 #include "nel/misc/fast_floor.h"
28 #include "nel/3d/tile_vegetable_desc.h"
29 #include "nel/3d/vegetable_light_ex.h"
30 #include "nel/3d/patchdlm_context.h"
34 using namespace NLMISC
;
44 // ***************************************************************************
45 void CPatch::generateTileVegetable(CVegetableInstanceGroup
*vegetIg
, uint distType
, uint ts
, uint tt
,
46 CLandscapeVegetableBlockCreateContext
&vbCreateCtx
)
50 // Get tile infos for vegetable
51 // =========================
53 // Get the state for this vegetable tile
54 CTileElement::TVegetableInfo vegetWaterState
= Tiles
[tt
* OrderS
+ ts
].getVegetableState();
55 // If vegetable disabled, skip!
56 if(vegetWaterState
== CTileElement::VegetableDisabled
)
59 // get the tileId under this tile (<=> the tile material)
60 uint tileId
= Tiles
[tt
* OrderS
+ ts
].Tile
[0];
62 // get list of vegetable for this tile, and for hist distanceType category.
63 const CTileVegetableDesc
&tileVegetDesc
= getLandscape()->getTileVegetableDesc(tileId
);
64 const std::vector
<CVegetable
> &vegetableList
= tileVegetDesc
.getVegetableList(distType
);
65 uint distAddSeed
= tileVegetDesc
.getVegetableSeed(distType
);
66 uint numVegetable
= (uint
)vegetableList
.size();
68 // If no vegetables at all, skip.
72 // If any layer (2nd or 3rd) is set, but has no vegetable, then skip too
73 // This is to ensure "no vegetable under buildings".
74 if( Tiles
[tt
* OrderS
+ ts
].Tile
[1]!=NL_TILE_ELM_LAYER_EMPTY
)
76 uint tileId1
= Tiles
[tt
* OrderS
+ ts
].Tile
[1];
77 uint tileId2
= Tiles
[tt
* OrderS
+ ts
].Tile
[2];
79 if(getLandscape()->getTileVegetableDesc(tileId1
).empty())
81 if(tileId2
!=NL_TILE_ELM_LAYER_EMPTY
&& getLandscape()->getTileVegetableDesc(tileId2
).empty())
86 // compute approximate tile position and normal: get the middle
87 float tileU
= (ts
+ 0.5f
) / (float)OrderS
;
88 float tileV
= (tt
+ 0.5f
) / (float)OrderT
;
89 CBezierPatch
*bpatch
= unpackIntoCache();
90 // Get approximate position for the tile (useful for noise). NB: eval() is faster than computeVertex().
91 CVector tilePos
= bpatch
->eval(tileU
, tileV
);
92 // Get also the normal used for all instances on this tile (not precise,
93 // don't take noise into account, but faster).
94 CVector tileNormal
= bpatch
->evalNormal(tileU
, tileV
);
96 // Compute also position on middle of 4 edges of this tile, for generateGroupBiLinear().
97 CVector tilePosBiLinear
[4];
98 float OOos
= 1.0f
/ OrderS
;
99 float OOot
= 1.0f
/ OrderT
;
100 tilePosBiLinear
[0]= bpatch
->eval( (ts
+ 0.0f
) * OOos
, (tt
+ 0.5f
) * OOot
);
101 tilePosBiLinear
[1]= bpatch
->eval( (ts
+ 1.0f
) * OOos
, (tt
+ 0.5f
) * OOot
);
102 tilePosBiLinear
[2]= bpatch
->eval( (ts
+ 0.5f
) * OOos
, (tt
+ 0.0f
) * OOot
);
103 tilePosBiLinear
[3]= bpatch
->eval( (ts
+ 0.5f
) * OOos
, (tt
+ 1.0f
) * OOot
);
106 // compute a rotation matrix with the normal
108 matInstance
.setRot(CVector::I
, CVector::J
, tileNormal
);
109 // must normalize the matrix. use the vector which is the most orthogonal to tileNormal
110 // If tileNormal is much more a J vector, then use plane (I,tileNormal), and vice-versa
111 if(fabs(tileNormal
.y
) > fabs(tileNormal
.x
))
112 matInstance
.normalize(CMatrix::ZXY
);
114 matInstance
.normalize(CMatrix::ZYX
);
117 // prepare color / lighting
118 // =========================
120 // say that ambient never change. VegetableManager handle the ambient and diffuse itself (for precomputeLighting)
121 CRGBAF ambientF
= CRGBAF(1,1,1,1);
123 // Compute the tileLightmap (not modified by tileColor).
124 static uint8 tileLumelmap
[NL_LUMEL_BY_TILE
* NL_LUMEL_BY_TILE
];
125 getTileLumelmapPrecomputed(ts
, tt
, tileLumelmap
, NL_LUMEL_BY_TILE
);
126 // compute diffuse color by substracting from ambient.
127 CRGBAF diffuseColorF
[NL_LUMEL_BY_TILE
* NL_LUMEL_BY_TILE
];
128 // TODO_VEGET_OPTIM: optimize this.
129 // For all lumel of this tile.
130 for(i
= 0; i
<NL_LUMEL_BY_TILE
*NL_LUMEL_BY_TILE
; i
++)
132 // mul by 2, because shade is done twice here: by vertex, and by landscape.
133 sint tileLumel
= 2*tileLumelmap
[i
];
134 tileLumel
= min(tileLumel
, 255);
135 float tlf
= tileLumel
/ 255.f
;
136 diffuseColorF
[i
].R
= tlf
;
137 diffuseColorF
[i
].G
= tlf
;
138 diffuseColorF
[i
].B
= tlf
;
139 diffuseColorF
[i
].A
= 1;
142 // Compute The CVegetableLightEx, adding pointLight effect to vegetation
143 // First get pointLight at this tiles.
144 static vector
<CPointLightInfluence
> lightList
;
146 appendTileLightInfluences( CUV(tileU
, tileV
), lightList
);
147 // for each light, modulate the factor of influence
148 for(i
=0; i
<lightList
.size();i
++)
150 CPointLight
*pl
= lightList
[i
].PointLight
;
151 // compute the attenuation to the pos of the tile
152 float att
= pl
->computeLinearAttenuation(tilePos
);
153 // modulate the influence with this factor
154 lightList
[i
].BkupInfluence
= lightList
[i
].Influence
;
155 lightList
[i
].Influence
*= att
;
157 // sort the light by influence
158 sort(lightList
.begin(), lightList
.end());
159 // Setup the vegetLex directly in the ig.
160 CVegetableLightEx
&vegetLex
= vegetIg
->VegetableLightEx
;
161 // take only 2 first, computing direction to tilePos and computing attenuation.
162 vegetLex
.NumLights
= min((uint
)CVegetableLightEx::MaxNumLight
, (uint
)lightList
.size());
163 for(i
=0;i
<vegetLex
.NumLights
;i
++)
165 // WARNING: can C cast to CPointLightNamed here because comes from CPatch::appendTileLightInfluences() only!
166 CPointLightNamed
*pl
= (CPointLightNamed
*)(lightList
[i
].PointLight
);
168 vegetLex
.PointLight
[i
]= pl
;
169 // get the attenuation
170 vegetLex
.PointLightFactor
[i
]= (uint
)(256* lightList
[i
].Influence
);
171 // Setup the direction from pointLight.
172 vegetLex
.Direction
[i
]= tilePos
- pl
->getPosition();
173 vegetLex
.Direction
[i
].normalize();
175 // compute now the current colors of the vegetLex.
176 vegetLex
.computeCurrentColors();
179 // Compute Dynamic Lightmap UV for this tile.
180 nlassert(_DLMContext
);
182 // get coordinate in 0..1 in texture.
183 dlmUV
.U
= _DLMContext
->DLMUBias
+ _DLMContext
->DLMUScale
* tileU
;
184 dlmUV
.V
= _DLMContext
->DLMVBias
+ _DLMContext
->DLMVScale
* tileV
;
185 // get coordinate in 0..255.
186 CVegetableUV8 dlmUV8
;
187 dlmUV8
.U
= (uint8
)NLMISC::OptFastFloor(dlmUV
.U
* 255 + 0.5f
);
188 dlmUV8
.V
= (uint8
)NLMISC::OptFastFloor(dlmUV
.V
* 255 + 0.5f
);
189 // bound them, ensuring 8Bits UV "uncompressed" by driver are in the lightmap area.
190 clamp(dlmUV8
.U
, _DLMContext
->MinU8
, _DLMContext
->MaxU8
);
191 clamp(dlmUV8
.V
, _DLMContext
->MinV8
, _DLMContext
->MaxV8
);
194 // for all vegetable of this list, generate instances.
195 // =========================
197 // Get an array for each vegetable (static for speed).
198 typedef std::vector
<NLMISC::CVector2f
> TPositionVector
;
199 static std::vector
<TPositionVector
> instanceUVArray
;
200 // realloc if necessary.
201 if(instanceUVArray
.size() < numVegetable
)
204 contReset(instanceUVArray
);
206 instanceUVArray
.resize(numVegetable
);
209 // First, for each vegetable, generate the number of instance to create, and their relative position.
210 for(i
= 0; i
<numVegetable
; i
++)
213 const CVegetable
&veget
= vegetableList
[i
];
215 // generate instance for this vegetable.
216 veget
.generateGroupBiLinear(tilePos
, tilePosBiLinear
, tileNormal
, NL3D_PATCH_TILE_AREA
, i
+ distAddSeed
, instanceUVArray
[i
]);
219 // Then, now that we kno how many instance to generate for each vegetable, reserve space.
220 CVegetableInstanceGroupReserve vegetIgReserve
;
221 for(i
= 0; i
<numVegetable
; i
++)
224 const CVegetable
&veget
= vegetableList
[i
];
226 // reseve instance space for this vegetable.
227 // instanceUVArray[i].size() is the number of instances to create.
228 veget
.reserveIgAddInstances(vegetIgReserve
, (CVegetable::TVegetableWater
)vegetWaterState
, (uint
)instanceUVArray
[i
].size());
230 // actual reseve memory of the ig.
231 getLandscape()->_VegetableManager
->reserveIgCompile(vegetIg
, vegetIgReserve
);
234 // generate the instances for all the vegetables.
235 for(i
= 0; i
<numVegetable
; i
++)
238 const CVegetable
&veget
= vegetableList
[i
];
240 // get the relatives position of the instances
241 std::vector
<CVector2f
> &instanceUV
= instanceUVArray
[i
];
243 // For all instance, generate the real instances.
244 for(uint j
=0; j
<instanceUV
.size(); j
++)
246 // generate the position in world Space.
247 // instanceUV is in [0..1] interval, which maps to a tile, so explode to the patch
249 vbCreateCtx
.eval(ts
, tt
, instanceUV
[j
].x
, instanceUV
[j
].y
, instancePos
);
250 // NB: use same normal for rotation for all instances in a same tile.
251 matInstance
.setPos( instancePos
);
253 // peek color into the lightmap.
254 sint lumelS
= NLMISC::OptFastFloor(instanceUV
[j
].x
* NL_LUMEL_BY_TILE
);
255 sint lumelT
= NLMISC::OptFastFloor(instanceUV
[j
].y
* NL_LUMEL_BY_TILE
);
256 clamp(lumelS
, 0, NL_LUMEL_BY_TILE
-1);
257 clamp(lumelT
, 0, NL_LUMEL_BY_TILE
-1);
259 // generate the instance of the vegetable
260 veget
.generateInstance(vegetIg
, matInstance
, ambientF
,
261 diffuseColorF
[ (lumelT
<<NL_LUMEL_BY_TILE_SHIFT
) + lumelS
],
262 (distType
+1) * NL3D_VEGETABLE_BLOCK_ELTDIST
, (CVegetable::TVegetableWater
)vegetWaterState
, dlmUV8
);
268 // ***************************************************************************
269 void CPatch::recreateAllVegetableIgs()
271 // For all TessBlocks, try to release their VegetableBlock
272 for(uint numtb
=0; numtb
<TessBlocks
.size(); numtb
++)
274 // if the vegetableBlock is deleted, and if there is at least one Material in the tessBlock, and if possible
275 if( TessBlocks
[numtb
].VegetableBlock
==NULL
&& TessBlocks
[numtb
].TileMaterialRefCount
>0
276 && getLandscape()->isVegetableActive())
278 // compute tessBlock coordinate
279 uint tbWidth
= OrderS
>>1;
280 uint ts
= numtb
&(tbWidth
-1);
281 uint tt
= numtb
/tbWidth
;
282 // crate the vegetable with tilecooridante (ie tessBlock coord *2);
283 createVegetableBlock(numtb
, ts
*2, tt
*2);
290 // ***************************************************************************
291 void CPatch::deleteAllVegetableIgs()
293 // For all TessBlocks, try to release their VegetableBlock
294 for(uint i
=0; i
<TessBlocks
.size(); i
++)
296 releaseVegetableBlock(i
);
302 // ***************************************************************************
303 void CPatch::createVegetableBlock(uint numTb
, uint ts
, uint tt
)
306 uint tbWidth
= OrderS
>> 1;
308 uint nTbPerCb
= NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK
;
309 uint cbWidth
= (tbWidth
+ nTbPerCb
-1) >> NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK_SHIFT
;
311 // compute tessBlock coordinate.
315 // compute clipBlock coordinate.
317 cbs
= tbs
>> NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK_SHIFT
;
318 cbt
= tbt
>> NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK_SHIFT
;
320 // create the vegetable block.
321 CLandscapeVegetableBlock
*vegetBlock
= new CLandscapeVegetableBlock
;
322 // Init / append to list.
323 // compute center of the vegetableBlock (approx).
324 CBezierPatch
*bpatch
= unpackIntoCache();
325 CVector center
= bpatch
->eval( (float)(tbs
*2+1)/OrderS
, (float)(tbt
*2+1)/OrderT
);
326 // Lower-Left tile is (tbs*2, tbt*2)
327 vegetBlock
->init(center
, getLandscape()->_VegetableManager
, VegetableClipBlocks
[cbt
*cbWidth
+ cbs
], this, tbs
*2, tbt
*2, getLandscape()->_VegetableBlockList
);
329 // set in the tessBlock
330 TessBlocks
[numTb
].VegetableBlock
= vegetBlock
;
334 // ***************************************************************************
335 void CPatch::releaseVegetableBlock(uint numTb
)
337 // if exist, must delete the VegetableBlock.
338 if(TessBlocks
[numTb
].VegetableBlock
)
340 // delete Igs, and remove from list.
341 TessBlocks
[numTb
].VegetableBlock
->release(getLandscape()->_VegetableManager
, getLandscape()->_VegetableBlockList
);
343 delete TessBlocks
[numTb
].VegetableBlock
;
344 TessBlocks
[numTb
].VegetableBlock
= NULL
;