Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / vegetable_manager.cpp
blob1bc0701dd0edca5e91ec9c2be9faec5487cb3577
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
23 #include "nel/3d/vegetable_manager.h"
24 #include "nel/3d/driver.h"
25 #include "nel/3d/texture_file.h"
26 #include "nel/misc/fast_floor.h"
27 #include "nel/3d/vegetable_quadrant.h"
28 #include "nel/3d/dru.h"
29 #include "nel/3d/radix_sort.h"
30 #include "nel/3d/scene.h"
31 #include "nel/3d/vegetable_blend_layer_model.h"
32 #include "nel/3d/vegetable_light_ex.h"
33 #include "nel/misc/hierarchical_timer.h"
35 #include <algorithm>
38 using namespace std;
39 using namespace NLMISC;
41 #ifdef DEBUG_NEW
42 #define new DEBUG_NEW
43 #endif
45 namespace NL3D
49 #define NL3D_VEGETABLE_CLIP_BLOCK_BLOCKSIZE 16
50 #define NL3D_VEGETABLE_SORT_BLOCK_BLOCKSIZE 64
51 #define NL3D_VEGETABLE_INSTANCE_GROUP_BLOCKSIZE 128
54 // ***************************************************************************
55 CVegetableManager::CVegetableManager(uint maxVertexVbHardUnlit, uint maxVertexVbHardLighted,
56 uint nbBlendLayers, float blendLayerDistMax) :
57 _ClipBlockMemory(NL3D_VEGETABLE_CLIP_BLOCK_BLOCKSIZE),
58 _SortBlockMemory(NL3D_VEGETABLE_SORT_BLOCK_BLOCKSIZE),
59 _InstanceGroupMemory(NL3D_VEGETABLE_INSTANCE_GROUP_BLOCKSIZE),
60 _GlobalDensity(1.f),
61 _NumZSortBlendLayers(nbBlendLayers), _ZSortLayerDistMax(blendLayerDistMax),
62 _ZSortScene(NULL)
64 uint i;
66 // Init all the allocators
67 nlassert((uint)(CVegetableVBAllocator::VBTypeCount) == 2);
68 _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted].init( CVegetableVBAllocator::VBTypeLighted, maxVertexVbHardLighted );
69 _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit].init( CVegetableVBAllocator::VBTypeUnlit, maxVertexVbHardUnlit );
70 // Init soft one, with no vbHard vertices.
71 _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted].init( CVegetableVBAllocator::VBTypeLighted, 0 );
72 _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit].init( CVegetableVBAllocator::VBTypeUnlit, 0 );
74 // NB Vertex programs are initilized during the first call to update driver.
76 // setup the material. Unlit (doesn't matter, lighting in VP) Alpha Test.
77 _VegetableMaterial.initUnlit();
78 _VegetableMaterial.setAlphaTest(true);
79 _VegetableMaterial.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha);
81 // default light.
82 _DirectionalLight= (CVector(0,1, -1)).normed();
83 _GlobalAmbient.set(64, 64, 64, 255);
84 _GlobalDiffuse.set(150, 150, 150, 255);
86 // Wind.
87 _WindDirection.set(1,0,0);
88 _WindFrequency= 1;
89 _WindPower= 1;
90 _WindBendMin= 0;
91 _Time= 0;
92 _WindPrecRenderTime= 0;
93 _WindAnimTime= 0;
95 // Init CosTable.
96 for(i=0; i<NL3D_VEGETABLE_VP_LUT_SIZE; i++)
98 _CosTable[i]= (float)cos( i*2*Pi / NL3D_VEGETABLE_VP_LUT_SIZE );
101 // init to NULL _ZSortModelLayers.
102 _NumZSortBlendLayers= max(1U, _NumZSortBlendLayers);
103 _ZSortModelLayers.resize(_NumZSortBlendLayers, NULL);
104 _ZSortModelLayersUW.resize(_NumZSortBlendLayers, NULL);
107 // UL
108 _ULFrequency= 0;
109 _ULNVerticesToUpdate=0;
110 _ULNTotalVertices= 0;
111 _ULRootIg= NULL;
112 _ULCurrentIgRdrPass= 0;
113 _ULCurrentIgInstance= 0;
114 _ULPrecTime= 0;
115 _ULPrecTimeInit= false;
116 _ULTime= 0;
118 // Misc.
119 _NumVegetableFaceRendered= 0;
121 for (uint k = 0; k < NL3D_VEGETABLE_NRDRPASS; ++k)
123 _VertexProgram[k][0] = NULL;
124 _VertexProgram[k][1] = NULL;
129 // ***************************************************************************
130 CVegetableManager::~CVegetableManager()
132 // delete All VP
133 for(sint i=0; i <NL3D_VEGETABLE_NRDRPASS; i++)
135 _VertexProgram[i][0] = NULL; // smart ptr
136 _VertexProgram[i][1] = NULL;
139 // delete ZSort models.
140 if(_ZSortScene)
142 // remove models from scene.
143 for(uint i= 0; i<_NumZSortBlendLayers; i++)
145 _ZSortScene->deleteModel(_ZSortModelLayers[i]);
146 _ZSortModelLayers[i]= NULL;
147 _ZSortScene->deleteModel(_ZSortModelLayersUW[i]);
148 _ZSortModelLayersUW[i]= NULL;
151 _ZSortScene= NULL;
156 // ***************************************************************************
157 void CVegetableManager::createVegetableBlendLayersModels(CScene *scene)
159 // setup scene
160 nlassert(scene);
161 _ZSortScene= scene;
163 // create the layers models.
164 for(uint i=0;i<_NumZSortBlendLayers; i++)
166 // assert not already done.
167 nlassert(_ZSortModelLayers[i]==NULL);
168 nlassert(_ZSortModelLayersUW[i]==NULL);
170 _ZSortModelLayers[i]= (CVegetableBlendLayerModel*)scene->createModel(VegetableBlendLayerModelId);
171 _ZSortModelLayersUW[i]= (CVegetableBlendLayerModel*)scene->createModel(VegetableBlendLayerModelId);
172 // init owner.
173 _ZSortModelLayers[i]->VegetableManager= this;
174 _ZSortModelLayersUW[i]->VegetableManager= this;
176 // Set UnderWater layer for _ZSortModelLayersUW
177 _ZSortModelLayersUW[i]->setOrderingLayer(2);
182 // ***************************************************************************
183 CVegetableVBAllocator &CVegetableManager::getVBAllocatorForRdrPassAndVBHardMode(uint rdrPass, uint vbHardMode)
185 // If software VB
186 if(vbHardMode==0)
188 if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED)
189 return _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted];
190 if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED)
191 return _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted];
192 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT)
193 return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit];
194 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED)
195 return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit];
196 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
197 return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit];
199 // If hard VB
200 else
202 if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED)
203 return _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted];
204 if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED)
205 return _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted];
206 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT)
207 return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit];
208 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED)
209 return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit];
210 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
211 return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit];
214 // abnormal case
215 nlstop;
216 // To avoid warning;
217 return _VBSoftAllocator[0];
222 // ***************************************************************************
223 // ***************************************************************************
224 // Vertex Program.
225 // ***************************************************************************
226 // ***************************************************************************
229 // ***************************************************************************
231 Vegetable, without bend for now.
233 Inputs
234 --------
235 v[0] == Pos to Center of the vegetable in world space.
236 v[10] == Center of the vegetable in world space.
237 v[2] == Normal (present if lighted only)
238 v[3] == Color (if unlit) or DiffuseColor (if lighted)
239 v[4] == SecondaryColor (==ambient if Lighted, and use only Alpha part for DLM if Unlit)
240 v[8] == Tex0 (xy)
241 v[9] == BendInfo (xyz) = {BendWeight/2, BendPhase, BendFrequencyFactor}
242 NB: /2 because compute a quaternion
244 Changes: If unlit, then small changes:
245 v[0] == Pos to center, with v[0].w == BendWeight * v[0].norm()
246 v[9] == BendInfo/BlendInfo (xyzw) = {v[0].norm(), BendPhase, BendFrequencyFactor, BlendDist}
248 NB: v[9].w. is used only in Unlit+2Sided+AlphaBlend. But prefer do this for gestion purpose:
249 to have only one VBAllocator for all modes.
251 NB: Color and Secondary color Alpha Part contains Dynamic LightMap UV, (in 8 bits).
253 Constant:
254 --------
255 Setuped at beginning of CVegetableManager::render()
256 c[0..3]= ModelViewProjection Matrix.
257 c[6]= Fog vector.
258 c[8]= {0, 1, 0.5, 2}
259 c[9]= unit world space Directionnal light.
260 c[10]= camera pos in world space.
261 c[11]= {1/DistBlendTransition}
262 NB: DiffuseColor and AmbientColor of vertex must have been pre-multiplied by lightColor
264 // Bend:
265 c[16]= quaternion axis. w==1, and z must be 0
266 c[17]= { timeAnim , WindPower, WindPower*(1-WindBendMin)/2, 0 }
267 c[18]= High order Taylor cos coefficient: { -1/2, 1/24, -1/720, 1/40320 }
268 c[19]= Low order Taylor cos coefficient: { 1, -1/2, 1/24, -1/720 }
269 c[20]= Low order Taylor sin coefficient: { 1, -1/6, 1/120, -1/5040 }
270 c[21]= Special constant vector for quatToMatrix: { 0, 1, -1, 0 }
271 c[22]= {0.5, Pi, 2*Pi, 1/(2*Pi)}
272 c[23]= {64, 0, 0, 0} (size of the LUT)
274 // Bend Lut:
275 c[32..95] 64 Lut entries for cos-like animation
278 Fog Note:
279 -----------
280 Fog should be disabled, because not computed (for speed consideration and becasue micro-vegetation should never
281 be in Fog).
284 Speed Note:
285 -----------
286 Max program length (lighted/2Sided) is:
287 29 (bend-quaternion) +
288 16 (rotNormal + bend + lit 2Sided) +
289 5 (proj + tex)
290 2 (Dynamic lightmap copy)
293 Normal program length (unlit/2Sided/No Alpha Blend) is:
294 12 (bend-delta) +
295 1 (unlit) +
296 5 (proj + tex)
297 2 (Dynamic lightmap copy)
300 AlphaBlend program length (unlit/2Sided/Alpha Blend) is:
301 12 (bend-delta) +
302 1 (unlit) +
303 5 (Alpha Blend)
304 5 (proj + tex)
305 2 (Dynamic lightmap copy)
311 // ***********************
313 Fast (but less accurate) Bend program:
314 Result: bend pos into R5,
316 // ***********************
317 const char* NL3D_FastBendProgram=
318 "!!VP1.0 \n\
319 # compute time of animation: time*freqfactor + phase. \n\
320 MAD R0.x, c[17].x, v[9].z, v[9].y; # R0.x= time of animation \n\
322 # animation: use the 64 LUT entries \n\
323 EXP R0.y, R0.x; # fract part=> R0.y= [0,1[ \n\
324 MUL R0, R0.y, c[23].xyyy; # R0.x= [0,64[ \n\
325 ARL A0.x, R0.x; # A0.x= index in the LUT \n\
326 EXP R0.y, R0.x; # R0.y= R0.x-A0.x= fp (fract part) \n\
327 # lookup and lerp in one it: R0= value + fp * dv. \n\
328 MAD R0.xy, R0.y, c[A0.x+32].zwww, c[A0.x+32].xyww; \n\
330 # The direction and power of the wind is encoded in the LUT. \n\
331 # Scale now by vertex BendFactor (stored in v[0].w) \n\
332 MAD R5, R0, v[0].w, v[0].xyzw; \n\
333 # compute 1/norm, and multiply by original norm stored in v[9].x \n\
334 DP3 R0.x, R5, R5; \n\
335 RSQ R0.x, R0.x; \n\
336 MUL R0.x, R0.x, v[9].x; \n\
337 # mul by this factor, and add to center \n\
338 MAD R5, R0.xxxw, R5, v[10]; \n\
340 # make local to camera pos. Important for ZBuffer precision \n\
341 ADD R5, R5, -c[10]; \n\
345 // Test
346 /*const char* NL3D_FastBendProgram=
347 "!!VP1.0 \n\
348 # compute time of animation: time + phase. \n\
349 ADD R0.x, c[17].x, v[9].y; # R0.x= time of animation \n\
351 # animation: f(x)= cos(x). compute a high precision cosinus \n\
352 EXP R0.y, R0.x; # fract part=> R0.y= [0,1] <=> [-Pi, Pi] \n\
353 MAD R0.x, R0.y, c[22].z, -c[22].y; # R0.x= a= [-Pi, Pi] \n\
354 # R0 must get a2, a4, a6, a8 \n\
355 MUL R0.x, R0.x, R0.x; # R0.x= a2 \n\
356 MUL R0.y, R0.x, R0.x; # R0= a2, a4 \n\
357 MUL R0.zw, R0.y, R0.xxxy; # R0= a2, a4, a6, a8 \n\
358 # Taylor serie: cos(x)= 1 - (1/2) a2 + (1/24) a4 - (1/720) a6 + (1/40320) a8. \n\
359 DP4 R0.x, R0, c[18]; # R0.x= cos(x) - 1. \n\
362 # original norm \n\
363 DP3 R2.x, v[0], v[0]; \n\
364 RSQ R2.y, R2.x; \n\
365 MUL R2.x, R2.x, R2.y; \n\
366 # norm, mul by factor, and add to relpos \n\
367 ADD R1.x, R0.x, c[8].w; \n\
368 MUL R0.x, v[9].x, R2.x; \n\
369 MUL R1, R1, R0.x; \n\
370 ADD R5.xyz, R1, v[0]; \n\
371 # mod norm \n\
372 DP3 R0.x, R5, R5; \n\
373 RSQ R0.x, R0.x; \n\
374 MUL R0.x, R0.x, R2.x; \n\
375 MAD R5, R0.x, R5, v[10]; \n\
376 ";*/
380 // ***********************
382 Bend start program:
383 Result: bend pos into R5, and R7,R8,R9 is the rotation matrix for possible normal lighting.
385 // ***********************
386 // Splitted in 2 parts because of the 2048 char limit
387 const char* NL3D_BendProgramP0=
388 "!!VP1.0 \n\
389 # compute time of animation: time*freqfactor + phase. \n\
390 MAD R0.x, c[17].x, v[9].z, v[9].y; # R0.x= time of animation \n\
392 # animation: f(x)= cos(x). compute a high precision cosinus \n\
393 EXP R0.y, R0.x; # fract part=> R0.y= [0,1] <=> [-Pi, Pi] \n\
394 MAD R0.x, R0.y, c[22].z, -c[22].y; # R0.x= a= [-Pi, Pi] \n\
395 # R0 must get a2, a4, a6, a8 \n\
396 MUL R0.x, R0.x, R0.x; # R0.x= a2 \n\
397 MUL R0.y, R0.x, R0.x; # R0= a2, a4 \n\
398 MUL R0.zw, R0.y, R0.xxxy; # R0= a2, a4, a6, a8 \n\
399 # Taylor serie: cos(x)= 1 - (1/2) a2 + (1/24) a4 - (1/720) a6 + (1/40320) a8. \n\
400 DP4 R0.x, R0, c[18]; # R0.x= cos(x) - 1. \n\
402 # R0.x= [-2, 0]. And we want a result in BendWeight/2*WindPower*[WindBendMin, 1] \n\
403 MAD R0.x, R0.x, c[17].z, c[17].y; # R0.x= WindPower*[WindBendMin, 1] \n\
404 MUL R0.x, R0.x, v[9].x; # R0.x= angle= BendWeight/2*WindPower*[WindBendMin, 1] \n\
406 # compute good precision sinus and cosinus, in R0.xy. \n\
407 # suppose that BendWeightMax/2== 2Pi/3 => do not need to fmod() nor \n\
408 # to have high order taylor serie \n\
409 DST R1.xy, R0.x, R0.x; # R1= 1, a2 \n\
410 MUL R1.z, R1.y, R1.y; # R1= 1, a2, a4 \n\
411 MUL R1.w, R1.y, R1.z; # R1= 1, a2, a4, a6 (cos serie) \n\
412 MUL R2, R1, R0.x; # R2= a, a3, a5, a7 (sin serie) \n\
413 DP4 R0.x, R1, c[19]; # R0.x= cos(a) \n\
414 DP4 R0.y, R2, c[20]; # R0.y= sin(a) \n\
416 const char* NL3D_BendProgramP1=
417 " \n\
418 # build our quaternion \n\
419 # multiply the angleAxis by sin(a) / cos(a), where a is actually a/2 \n\
420 # remind: c[16].z== angleAxis.z== 0 \n\
421 MUL R0, c[16], R0.yyyx; # R0= quaternion.xyzw \n\
423 # build our matrix from this quaternion, into R7,R8,R9 \n\
424 # Quaternion TO matrix 3x3 in 7 ope, with quat.z==0 \n\
425 MUL R1, R0, c[8].w; # R1= quat2= 2*quat == 2*x, 2*y, 0, 2*w \n\
426 MUL R2, R1, R0.x; # R2= quatX= xx, xy, 0, wx \n\
427 MUL R3, R1, R0.y; # R3= quatY= xy, yy, 0, wy \n\
428 # NB: c[21]= {0, 1, -1, 0} \n\
429 # Write to w, then w = 0, this avoid an unitialized component \n\
430 MAD R7.xyzw, c[21].zyyw, R3.yxww, c[21].yxxw; \n\
431 # R7.x= a11 = 1.0f - (yy) \n\
432 # R7.y= a12 = xy \n\
433 # R7.z= a13 = wy \n\
434 # NB: c[21]= {0, 1, -1, 0} \n\
435 # Write to w, then w = 0, this avoid an unitialized component \n\
436 MAD R8.xyzw, c[21].yzzw, R2.yxww, c[21].xyxw; \n\
437 # R8.x= a21 = xy \n\
438 # R8.y= a22 = 1.0f - (xx) \n\
439 # R8.z= a23 = - wx \n\
440 # NB: c[21]= {0, 1, -1, 0} \n\
441 # Write to w, then w = 0, this avoid an unitialized component \n\
442 ADD R9.xyzw, R2.zwxw, R3.wzyw; # a31= 0+wy, a32= wx+0, a33= xx + yy, because z==0 \n\
443 MAD R9.xyzw, R9.xyzw, c[21].zyzw, c[21].xxyw; \n\
444 # R9.x= a31 = - wy \n\
445 # R9.y= a32 = wx \n\
446 # R9.z= a33 = 1.0f - (xx + yy) \n\
447 # transform pos \n\
448 DP3 R5.x, R7, v[0]; \n\
449 DP3 R5.y, R8, v[0]; \n\
450 DP3 R5.z, R9, v[0]; # R5= bended relative pos to center. \n\
451 #temp, to optimize \n\
452 MOV R5.w, c[21].w; \n\
453 # add pos to center pos. \n\
454 ADD R5, R5, v[10]; # R5= world pos. R5.w= R5.w+v[10].w= 0+1= 1 \n\
455 # make local to camera pos. Important for ZBuffer precision \n\
456 ADD R5, R5, -c[10]; \n\
460 // Concat the 2 strings
461 const string NL3D_BendProgram= string(NL3D_BendProgramP0) + string(NL3D_BendProgramP1);
465 // ***********************
467 Lighted start program:
468 bend pos and normal, normalize and lit
470 // ***********************
471 // Common start program.
472 const char* NL3D_LightedStartVegetableProgram=
473 " \n\
474 # bend Pos into R5. Now do it for normal \n\
475 DP3 R0.x, R7, v[2]; \n\
476 DP3 R0.y, R8, v[2]; \n\
477 DP3 R0.z, R9, v[2]; # R0= matRot * normal. \n\
478 # Do the rot 2 times for normal (works fine) \n\
479 DP3 R6.x, R7, R0; \n\
480 DP3 R6.y, R8, R0; \n\
481 DP3 R6.z, R9, R0; # R6= bended normal. \n\
483 # Normalize normal, and dot product, into R0.x \n\
484 # w hasn't been written \n\
485 DP3 R0.x, R6.xyzz, R6.xyzz; # R0.x= R6.sqrnorm() \n\
486 RSQ R0.x, R0.x; # R0.x= 1/norm() \n\
487 MUL R6, R6.xyzz, R0.x; # R6= R6.normed() \n\
488 DP3 R0.x, R6, c[9]; \n\
490 #FrontFacing \n\
491 MAX R0.y, -R0.x, c[8].x; # R0.y= diffFactor= max(0, -R6*LightDir) \n\
492 MUL R1.xyz, R0.y, v[3]; # R7= diffFactor*DiffuseColor \n\
493 ADD o[COL0].xyz, R1, v[4]; # col0.RGB= AmbientColor + diffFactor*DiffuseColor \n\
494 MOV o[COL0].w, c[8].y; \n\
498 // ***********************
500 Unlit start program:
501 bend pos into R5, and copy color(s)
503 // ***********************
506 // Unlit no alpha blend.
507 const char* NL3D_UnlitVegetableProgram=
508 " MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\
510 MOV o[COL0].w, c[8].y; \n\
514 // Unlit with AlphaBlend.
515 const char* NL3D_UnlitAlphaBlendVegetableProgram=
516 " MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\
518 #Blend transition. NB: in R5, we already have the position relative to the camera \n\
519 DP3 R0.x, R5, R5; # R0.x= sqr(dist to viewer). \n\
520 RSQ R0.y, R0.x; \n\
521 MUL R0.x, R0.x, R0.y; # R0.x= dist to viewer \n\
522 # setup alpha Blending. Distance of appartition is encoded in the vertex. \n\
523 MAD o[COL0].w, R0.x, c[11].x, v[9].w; \n\
528 // ***********************
530 Common end of program: project, texture. Take pos from R5
532 // ***********************
533 const char* NL3D_CommonEndVegetableProgram=
534 " # compute in Projection space \n\
535 DP4 o[HPOS].x, c[0], R5; \n\
536 DP4 o[HPOS].y, c[1], R5; \n\
537 DP4 o[HPOS].z, c[2], R5; \n\
538 DP4 o[HPOS].w, c[3], R5; \n\
539 # copy Dynamic lightmap UV in stage0, from colors Alpha part. \n\
540 MAD o[TEX0].xzw, v[3].w, c[8].yxxx, c[8].xxxy; \n\
541 MOV o[TEX0].y, v[4].w; \n\
542 # copy diffuse texture uv to stage 1. \n\
543 MOV o[TEX1], v[8]; \n\
546 // fogged version
547 const char* NL3D_VegetableProgramFog =
548 " DP4 o[FOGC].x, c[6], R5; \n\
552 // ***********************
554 Speed test VP, No bend,no lighting.
556 // ***********************
557 const char* NL3D_SimpleStartVegetableProgram=
558 "!!VP1.0 \n\
559 # compute in Projection space \n\
560 MAD R5, v[0], c[8].yyyx, c[8].xxxy; \n\
561 ADD R5.xyz, R5, v[10]; \n\
562 # make local to camera pos \n\
563 ADD R5, R5, -c[10]; \n\
564 MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\
567 class CVertexProgramVeget : public CVertexProgram
569 public:
570 struct CIdx
572 // 0-3 modelViewProjection
573 // 4
574 // 5
575 // 6 fog
576 // 7
577 uint ProgramConstants0; // 8
578 uint DirectionalLight; // 9
579 uint ViewCenter; // 10
580 uint NegInvTransDist; // 11
581 // 12
582 // 13
583 // 14
584 // 15
585 uint AngleAxis; // 16
586 uint Wind; // 17
587 uint CosCoeff0; // 18
588 uint CosCoeff1; // 19
589 uint CosCoeff2; // 20
590 uint QuatConstants; // 21
591 uint PiConstants; // 22
592 uint LUTSize; // 23 (value = 64)
593 uint LUT[NL3D_VEGETABLE_VP_LUT_SIZE]; // 32+
595 CVertexProgramVeget(uint vpType, bool fogEnabled)
597 // nelvp
599 CSource *source = new CSource();
600 source->Profile = nelvp;
601 source->DisplayName = "nelvp/Veget";
603 // Init the Vertex Program.
604 string vpgram;
605 // start always with Bend.
606 if( vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED || vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED )
608 source->DisplayName += "/Bend";
609 vpgram= NL3D_BendProgram;
611 else
613 source->DisplayName += "/FastBend";
614 vpgram= NL3D_FastBendProgram;
617 // combine the VP according to Type
618 switch(vpType)
620 case NL3D_VEGETABLE_RDRPASS_LIGHTED:
621 case NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED:
622 source->DisplayName += "/Lighted";
623 vpgram+= string(NL3D_LightedStartVegetableProgram);
624 break;
625 case NL3D_VEGETABLE_RDRPASS_UNLIT:
626 case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED:
627 source->DisplayName += "/Unlit";
628 vpgram+= string(NL3D_UnlitVegetableProgram);
629 break;
630 case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT:
631 source->DisplayName += "/UnlitAlphaBlend";
632 vpgram+= string(NL3D_UnlitAlphaBlendVegetableProgram);
633 break;
636 // common end of VP
637 vpgram+= string(NL3D_CommonEndVegetableProgram);
639 if (fogEnabled)
641 source->DisplayName += "/Fog";
642 vpgram+= string(NL3D_VegetableProgramFog);
645 vpgram+="\nEND\n";
647 source->setSource(vpgram);
649 source->ParamIndices["modelViewProjection"] = 0;
650 source->ParamIndices["fog"] = 6;
651 source->ParamIndices["programConstants0"] = 8;
652 source->ParamIndices["directionalLight"] = 9;
653 source->ParamIndices["viewCenter"] = 10;
654 source->ParamIndices["negInvTransDist"] = 11;
655 source->ParamIndices["angleAxis"] = 16;
656 source->ParamIndices["wind"] = 17;
657 source->ParamIndices["cosCoeff0"] = 18;
658 source->ParamIndices["cosCoeff1"] = 19;
659 source->ParamIndices["cosCoeff2"] = 20;
660 source->ParamIndices["quatConstants"] = 21;
661 source->ParamIndices["piConstants"] = 22;
662 source->ParamIndices["lutSize"] = 23;
663 for (uint i = 0; i < NL3D_VEGETABLE_VP_LUT_SIZE; ++i)
665 source->ParamIndices[NLMISC::toString("lut[%i]", i)] = 32 + i;
668 addSource(source);
670 // TODO_VP_GLSL
672 virtual ~CVertexProgramVeget()
676 virtual void buildInfo()
678 m_Idx.ProgramConstants0 = getUniformIndex("programConstants0");
679 nlassert(m_Idx.ProgramConstants0 != std::numeric_limits<uint>::max());
680 m_Idx.DirectionalLight = getUniformIndex("directionalLight");
681 nlassert(m_Idx.DirectionalLight != std::numeric_limits<uint>::max());
682 m_Idx.ViewCenter = getUniformIndex("viewCenter");
683 nlassert(m_Idx.ViewCenter != std::numeric_limits<uint>::max());
684 m_Idx.NegInvTransDist = getUniformIndex("negInvTransDist");
685 nlassert(m_Idx.NegInvTransDist != std::numeric_limits<uint>::max());
686 m_Idx.AngleAxis = getUniformIndex("angleAxis");
687 nlassert(m_Idx.AngleAxis != std::numeric_limits<uint>::max());
688 m_Idx.Wind = getUniformIndex("wind");
689 nlassert(m_Idx.Wind != std::numeric_limits<uint>::max());
690 m_Idx.CosCoeff0 = getUniformIndex("cosCoeff0");
691 nlassert(m_Idx.CosCoeff0 != std::numeric_limits<uint>::max());
692 m_Idx.CosCoeff1 = getUniformIndex("cosCoeff1");
693 nlassert(m_Idx.CosCoeff1 != std::numeric_limits<uint>::max());
694 m_Idx.CosCoeff2 = getUniformIndex("cosCoeff2");
695 nlassert(m_Idx.CosCoeff2 != std::numeric_limits<uint>::max());
696 m_Idx.QuatConstants = getUniformIndex("quatConstants");
697 nlassert(m_Idx.QuatConstants != std::numeric_limits<uint>::max());
698 m_Idx.PiConstants = getUniformIndex("piConstants");
699 nlassert(m_Idx.PiConstants != std::numeric_limits<uint>::max());
700 m_Idx.LUTSize = getUniformIndex("lutSize");
701 nlassert(m_Idx.LUTSize != std::numeric_limits<uint>::max());
702 for (uint i = 0; i < NL3D_VEGETABLE_VP_LUT_SIZE; ++i)
704 m_Idx.LUT[i] = getUniformIndex(NLMISC::toString("lut[%i]", i));
705 nlassert(m_Idx.LUT[i] != std::numeric_limits<uint>::max());
708 const CIdx &idx() const { return m_Idx; }
709 private:
710 CIdx m_Idx;
713 // ***************************************************************************
714 void CVegetableManager::initVertexProgram(uint vpType, bool fogEnabled)
716 nlassert(_LastDriver); // update driver should have been called at least once !
718 // create VP.
719 _VertexProgram[vpType][fogEnabled ? 1 : 0] = new CVertexProgramVeget(vpType, fogEnabled);
723 // ***************************************************************************
724 // ***************************************************************************
725 // Instanciation
726 // ***************************************************************************
727 // ***************************************************************************
730 // ***************************************************************************
731 CVegetableClipBlock *CVegetableManager::createClipBlock()
733 // create a clipblock
734 CVegetableClipBlock *ret;
735 ret= _ClipBlockMemory.allocate();
737 // append to list.
738 _EmptyClipBlockList.append(ret);
740 return ret;
743 // ***************************************************************************
744 void CVegetableManager::deleteClipBlock(CVegetableClipBlock *clipBlock)
746 if(!clipBlock)
747 return;
749 // verify no more sortBlocks in this clipblock
750 nlassert(clipBlock->_SortBlockList.size() == 0);
752 // unlink from _EmptyClipBlockList, because _InstanceGroupList.size() == 0 ...
753 _EmptyClipBlockList.remove(clipBlock);
755 // delete
756 _ClipBlockMemory.freeBlock(clipBlock);
760 // ***************************************************************************
761 CVegetableSortBlock *CVegetableManager::createSortBlock(CVegetableClipBlock *clipBlock, const CVector &center, float radius)
763 nlassert(clipBlock);
765 // create a clipblock
766 CVegetableSortBlock *ret;
767 ret= _SortBlockMemory.allocate();
768 ret->_Owner= clipBlock;
769 ret->_Center= center;
770 ret->_Radius= radius;
772 // append to list.
773 clipBlock->_SortBlockList.append(ret);
775 return ret;
778 // ***************************************************************************
779 void CVegetableManager::deleteSortBlock(CVegetableSortBlock *sortBlock)
781 if(!sortBlock)
782 return;
784 // verify no more IGs in this sortblock
785 nlassert(sortBlock->_InstanceGroupList.size() == 0);
787 // unlink from clipBlock
788 sortBlock->_Owner->_SortBlockList.remove(sortBlock);
790 // delete
791 _SortBlockMemory.freeBlock(sortBlock);
795 // ***************************************************************************
796 CVegetableInstanceGroup *CVegetableManager::createIg(CVegetableSortBlock *sortBlock)
798 nlassert(sortBlock);
799 CVegetableClipBlock *clipBlock= sortBlock->_Owner;
802 // create an IG
803 CVegetableInstanceGroup *ret;
804 ret= _InstanceGroupMemory.allocate();
805 ret->_SortOwner= sortBlock;
806 ret->_ClipOwner= clipBlock;
808 // if the clipBlock is empty, change list, because won't be no more.
809 if(clipBlock->_NumIgs==0)
811 // remove from empty list
812 _EmptyClipBlockList.remove(clipBlock);
813 // and append to not empty one.
814 _ClipBlockList.append(clipBlock);
817 // inc the number of igs appended to the clipBlock.
818 clipBlock->_NumIgs++;
820 // link ig to sortBlock.
821 sortBlock->_InstanceGroupList.append(ret);
823 // Special Init: The ZSort rdrPass must start with the same HardMode than SortBlock.
824 ret->_RdrPass[NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT].HardMode= sortBlock->ZSortHardMode;
826 return ret;
829 // ***************************************************************************
830 void CVegetableManager::deleteIg(CVegetableInstanceGroup *ig)
832 if(!ig)
833 return;
835 // update lighting mgt: no more vertices.
836 // -----------
837 // If I delete the ig which is the current root
838 if(_ULRootIg == ig)
840 // switch to next
841 _ULRootIg= ig->_ULNext;
842 // if still the same, it means that the circular list is now empty
843 if(_ULRootIg == ig)
844 _ULRootIg= NULL;
845 // Reset UL instance info.
846 _ULCurrentIgRdrPass= 0;
847 _ULCurrentIgInstance= 0;
849 // remove UL vertex count of the deleted ig
850 _ULNTotalVertices-= ig->_ULNumVertices;
851 // unlink the ig for lighting update.
852 ig->unlinkUL();
855 // For all render pass of this instance, delete his vertices
856 // -----------
857 for(sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++)
859 // rdrPass
860 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass];
861 // which allocator?
862 CVegetableVBAllocator &vbAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vegetRdrPass.HardMode);
864 // For all vertices of this rdrPass, delete it
865 sint numVertices;
866 numVertices= vegetRdrPass.Vertices.size();
867 // all vertices must have been setuped.
868 nlassert((uint)numVertices == vegetRdrPass.NVertices);
869 for(sint i=0; i<numVertices;i++)
871 vbAllocator.deleteVertex(vegetRdrPass.Vertices[i]);
873 vegetRdrPass.Vertices.clear();
876 CVegetableClipBlock *clipBlock= ig->_ClipOwner;
877 CVegetableSortBlock *sortBlock= ig->_SortOwner;
879 // If I have got some faces in ZSort rdrPass
880 if(ig->_HasZSortPassInstances)
881 // after my deletion, the sortBlock must be updated.
882 sortBlock->_Dirty= true;
885 // unlink from sortBlock, and delete.
886 sortBlock->_InstanceGroupList.remove(ig);
887 _InstanceGroupMemory.freeBlock(ig);
890 // decRef the clipBlock
891 clipBlock->_NumIgs--;
892 // if the clipBlock is now empty, change list
893 if(clipBlock->_NumIgs==0)
895 // remove from normal list
896 _ClipBlockList.remove(clipBlock);
897 // and append to empty list.
898 _EmptyClipBlockList.append(clipBlock);
904 // ***************************************************************************
905 CVegetableShape *CVegetableManager::getVegetableShape(const std::string &shape)
907 ItShapeMap it= _ShapeMap.find(shape);
908 // if found
909 if(it != _ShapeMap.end())
910 return &it->second;
911 // else insert
913 // insert.
914 CVegetableShape *ret;
915 it= ( _ShapeMap.insert(make_pair(shape, CVegetableShape()) ) ).first;
916 ret= &it->second;
918 // fill.
921 if( !ret->loadShape(shape) )
923 // Warning
924 nlwarning ("CVegetableManager::getVegetableShape could not load shape file '%s'", shape.c_str ());
926 // Remove from map
927 _ShapeMap.erase (shape);
929 // Return NULL
930 ret = NULL;
933 catch (const Exception &e)
935 // Warning
936 nlwarning ("CVegetableManager::getVegetableShape error while loading shape file '%s' : '%s'", shape.c_str (), e.what ());
938 // Remove from map
939 _ShapeMap.erase (shape);
941 // Return NULL
942 ret = NULL;
945 return ret;
950 // ***************************************************************************
951 uint CVegetableManager::getRdrPassInfoForShape(CVegetableShape *shape, TVegetableWater vegetWaterState,
952 bool &instanceLighted, bool &instanceDoubleSided, bool &instanceZSort,
953 bool &destLighted, bool &precomputeLighting)
955 instanceLighted= shape->Lighted;
956 instanceDoubleSided= shape->DoubleSided;
957 // Disable ZSorting when we intersect water.
958 instanceZSort= shape->AlphaBlend && vegetWaterState!=IntersectWater;
959 destLighted= instanceLighted && !shape->PreComputeLighting;
960 precomputeLighting= instanceLighted && shape->PreComputeLighting;
962 // get correct rdrPass
963 uint rdrPass;
964 // get according to lighted / doubleSided state
965 if(destLighted)
967 if(instanceDoubleSided)
968 rdrPass= NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED;
969 else
970 rdrPass= NL3D_VEGETABLE_RDRPASS_LIGHTED;
972 else
974 if(instanceDoubleSided)
976 if(instanceZSort)
977 rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT;
978 else
979 rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED;
981 else
982 rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT;
985 return rdrPass;
989 // ***************************************************************************
990 void CVegetableManager::reserveIgAddInstances(CVegetableInstanceGroupReserve &vegetIgReserve, CVegetableShape *shape, TVegetableWater vegetWaterState, uint numInstances)
992 bool instanceLighted;
993 bool instanceDoubleSided;
994 bool instanceZSort;
995 bool destLighted;
996 bool precomputeLighting;
998 // get correct rdrPass / info
999 uint rdrPass;
1000 rdrPass= getRdrPassInfoForShape(shape, vegetWaterState, instanceLighted, instanceDoubleSided,
1001 instanceZSort, destLighted, precomputeLighting);
1003 // veget rdrPass
1004 CVegetableInstanceGroupReserve::CVegetableRdrPass &vegetRdrPass= vegetIgReserve._RdrPass[rdrPass];
1006 // Reserve space in the rdrPass.
1007 vegetRdrPass.NVertices+= numInstances * shape->VB.getNumVertices();
1008 vegetRdrPass.NTriangles+= numInstances * (uint)shape->TriangleIndices.size()/3;
1009 // if the instances are lighted, reserve space for lighting updates
1010 if(instanceLighted)
1011 vegetRdrPass.NLightedInstances+= numInstances;
1015 // ***************************************************************************
1016 void CVegetableManager::reserveIgCompile(CVegetableInstanceGroup *ig, const CVegetableInstanceGroupReserve &vegetIgReserve)
1018 uint rdrPass;
1021 // Check.
1022 //===========
1023 // For all rdrPass of the ig, check empty
1024 for(rdrPass= 0; rdrPass<NL3D_VEGETABLE_NRDRPASS; rdrPass++)
1026 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass];
1027 nlassert(vegetRdrPass.TriangleIndices.getNumIndexes()==0);
1028 nlassert(vegetRdrPass.TriangleLocalIndices.empty());
1029 nlassert(vegetRdrPass.Vertices.empty());
1030 nlassert(vegetRdrPass.LightedInstances.empty());
1032 // Do the same for all quadrants of the zsort rdrPass.
1033 nlassert(ig->_TriangleQuadrantOrderArray.empty());
1034 nlassert(ig->_TriangleQuadrantOrderNumTriangles==0);
1037 // Reserve.
1038 //===========
1039 // For all rdrPass of the ig, reserve.
1040 for(rdrPass= 0; rdrPass<NL3D_VEGETABLE_NRDRPASS; rdrPass++)
1042 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass];
1043 uint numVertices= vegetIgReserve._RdrPass[rdrPass].NVertices;
1044 uint numTris= vegetIgReserve._RdrPass[rdrPass].NTriangles;
1045 uint numLightedInstances= vegetIgReserve._RdrPass[rdrPass].NLightedInstances;
1046 // reserve triangles indices and vertices for this rdrPass.
1047 vegetRdrPass.TriangleIndices.setFormat(vegetRdrPass.HardMode ? CIndexBuffer::Indices16 : CIndexBuffer::Indices32);
1048 vegetRdrPass.TriangleIndices.setNumIndexes(numTris*3);
1049 vegetRdrPass.TriangleLocalIndices.resize(numTris*3);
1050 vegetRdrPass.Vertices.resize(numVertices);
1051 // reserve ligthedinstances space.
1052 vegetRdrPass.LightedInstances.resize(numLightedInstances);
1055 // Reserve space for the zsort rdrPass sorting.
1056 uint numZSortTris= vegetIgReserve._RdrPass[NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT].NTriangles;
1057 // allocate sufficient space for all quadrants (1 alloc for all quadrants).
1058 ig->_TriangleQuadrantOrderArray.resize(numZSortTris * NL3D_VEGETABLE_NUM_QUADRANT);
1060 // And init ptrs.
1061 if(numZSortTris>0)
1063 sint16 *start= ig->_TriangleQuadrantOrderArray.getPtr();
1064 // init ptr to each qaudrant
1065 for(uint i=0; i<NL3D_VEGETABLE_NUM_QUADRANT; i++)
1067 ig->_TriangleQuadrantOrders[i]= start + i*numZSortTris;
1073 // ***************************************************************************
1074 inline void computeVegetVertexLighting(const CVector &rotNormal,
1075 const CVector &sunDir, CRGBA primaryRGBA, CRGBA secondaryRGBA,
1076 CVegetableLightEx &vegetLex, CRGBA diffusePL[2], CRGBA *dst)
1078 float dpSun;
1079 float dpPL[2];
1080 CRGBA col;
1081 CRGBA resColor;
1084 // compute front-facing coloring.
1086 // Compute Sun Light.
1087 dpSun= rotNormal*sunDir;
1088 float f= max(0.f, -dpSun);
1089 col.modulateFromuiRGBOnly(primaryRGBA, NLMISC::OptFastFloor(f*256));
1090 // Add it with ambient
1091 resColor.addRGBOnly(col, secondaryRGBA);
1093 // Add influence of 2 lights only. (unrolled for better BTB use)
1094 // Compute Light 0 ?
1095 if(vegetLex.NumLights>=1)
1097 dpPL[0]= rotNormal*vegetLex.Direction[0];
1098 f= max(0.f, -dpPL[0]);
1099 col.modulateFromuiRGBOnly(diffusePL[0], NLMISC::OptFastFloor(f*256));
1100 resColor.addRGBOnly(col, resColor);
1101 // Compute Light 1 ?
1102 if(vegetLex.NumLights>=2)
1104 dpPL[1]= rotNormal*vegetLex.Direction[1];
1105 f= max(0.f, -dpPL[1]);
1106 col.modulateFromuiRGBOnly(diffusePL[1], NLMISC::OptFastFloor(f*256));
1107 resColor.addRGBOnly(col, resColor);
1111 // Keep correct U of Dynamic Lightmap UV encoded in primaryRGBA Alpha part.
1112 resColor.A= primaryRGBA.A;
1114 // copy to dest
1115 *dst= resColor;
1120 // ***************************************************************************
1121 inline void computeVegetVertexLightingForceBestSided(const CVector &rotNormal,
1122 const CVector &sunDir, CRGBA primaryRGBA, CRGBA secondaryRGBA,
1123 CVegetableLightEx &vegetLex, CRGBA diffusePL[2], CRGBA *dst)
1125 float dpSun;
1126 float dpPL[2];
1127 CRGBA col;
1128 CRGBA resColor;
1131 // compute best-facing coloring.
1133 // Compute Sun Light.
1134 dpSun= rotNormal*sunDir;
1135 // ForceBestSided: take the absolute value (max of -val,val)
1136 float f= (float)fabs(dpSun);
1137 col.modulateFromuiRGBOnly(primaryRGBA, NLMISC::OptFastFloor(f*256));
1138 // Add it with ambient
1139 resColor.addRGBOnly(col, secondaryRGBA);
1141 // Add influence of 2 lights only. (unrolled for better BTB use)
1142 // Compute Light 0 ?
1143 if(vegetLex.NumLights>=1)
1145 dpPL[0]= rotNormal*vegetLex.Direction[0];
1146 // ForceBestSided: take the absolute value (max of -val,val)
1147 f= (float)fabs(dpPL[0]);
1148 col.modulateFromuiRGBOnly(diffusePL[0], NLMISC::OptFastFloor(f*256));
1149 resColor.addRGBOnly(col, resColor);
1150 // Compute Light 1 ?
1151 if(vegetLex.NumLights>=2)
1153 dpPL[1]= rotNormal*vegetLex.Direction[1];
1154 f= (float)fabs(dpPL[1]);
1155 col.modulateFromuiRGBOnly(diffusePL[1], NLMISC::OptFastFloor(f*256));
1156 resColor.addRGBOnly(col, resColor);
1160 // Keep correct U of Dynamic Lightmap UV encoded in primaryRGBA Alpha part.
1161 resColor.A= primaryRGBA.A;
1163 // copy to dest
1164 *dst= resColor;
1170 // ***************************************************************************
1171 void CVegetableManager::addInstance(CVegetableInstanceGroup *ig,
1172 CVegetableShape *shape, const NLMISC::CMatrix &mat,
1173 const NLMISC::CRGBAF &ambientColor, const NLMISC::CRGBAF &diffuseColor,
1174 float bendFactor, float bendPhase, float bendFreqFactor, float blendDistMax,
1175 TVegetableWater vegetWaterState, CVegetableUV8 dlmUV)
1177 sint i;
1180 // Some setup.
1181 //--------------------
1182 bool instanceLighted;
1183 bool instanceDoubleSided;
1184 bool instanceZSort;
1185 bool destLighted;
1186 bool precomputeLighting;
1188 // get correct rdrPass / info
1189 uint rdrPass;
1190 rdrPass= getRdrPassInfoForShape(shape, vegetWaterState, instanceLighted, instanceDoubleSided,
1191 instanceZSort, destLighted, precomputeLighting);
1192 // bestSided Precompute lighting or not??
1193 bool bestSidedPrecomputeLighting= precomputeLighting && shape->BestSidedPreComputeLighting;
1196 // veget rdrPass
1197 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass];
1199 // color.
1200 // setup using OptFastFloor.
1201 CRGBA ambientRGBA, diffuseRGBA;
1202 CRGBA primaryRGBA, secondaryRGBA;
1203 // diffuseColor
1204 diffuseRGBA.R= (uint8)NLMISC::OptFastFloor(diffuseColor.R*255);
1205 diffuseRGBA.G= (uint8)NLMISC::OptFastFloor(diffuseColor.G*255);
1206 diffuseRGBA.B= (uint8)NLMISC::OptFastFloor(diffuseColor.B*255);
1207 diffuseRGBA.A= 255;
1208 // ambientColor
1209 ambientRGBA.R= (uint8)NLMISC::OptFastFloor(ambientColor.R*255);
1210 ambientRGBA.G= (uint8)NLMISC::OptFastFloor(ambientColor.G*255);
1211 ambientRGBA.B= (uint8)NLMISC::OptFastFloor(ambientColor.B*255);
1212 ambientRGBA.A= 255;
1214 // For Lighted, modulate with global light.
1215 if(instanceLighted)
1217 primaryRGBA.modulateFromColorRGBOnly(diffuseRGBA, _GlobalDiffuse);
1218 secondaryRGBA.modulateFromColorRGBOnly(ambientRGBA, _GlobalAmbient);
1220 // if the instance is not lighted, then don't take care of lighting
1221 else
1223 primaryRGBA.R= diffuseRGBA.R;
1224 primaryRGBA.G= diffuseRGBA.G;
1225 primaryRGBA.B= diffuseRGBA.B;
1226 // may not be useful (2Sided lighting no more supported)
1227 secondaryRGBA= primaryRGBA;
1230 // Copy Dynamic Lightmap UV in Alpha part (save memory for an extra cost of 1 VP instruction)
1231 primaryRGBA.A= dlmUV.U;
1232 secondaryRGBA.A= dlmUV.V;
1234 // get ref on the vegetLex.
1235 CVegetableLightEx &vegetLex= ig->VegetableLightEx;
1236 // Color of pointLights modulated by diffuse.
1237 CRGBA diffusePL[2];
1238 diffusePL[0] = CRGBA::Black;
1239 diffusePL[1] = CRGBA::Black;
1240 if(vegetLex.NumLights>=1)
1242 diffusePL[0].modulateFromColorRGBOnly(diffuseRGBA, vegetLex.Color[0]);
1243 if(vegetLex.NumLights>=2)
1245 diffusePL[1].modulateFromColorRGBOnly(diffuseRGBA, vegetLex.Color[1]);
1249 // normalize bendFreqFactor
1250 bendFreqFactor*= NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC;
1251 bendFreqFactor= (float)floor(bendFreqFactor + 0.5f);
1252 bendFreqFactor/= NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC;
1255 // Get allocator, and manage VBhard overriding.
1256 //--------------------
1257 CVegetableVBAllocator *allocator;
1258 // if still in Sfot mode, keep it.
1259 if(!vegetRdrPass.HardMode)
1261 // get the soft allocator.
1262 allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 0);
1264 else
1266 // Get VB allocator Hard for this rdrPass
1267 allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 1);
1268 // Test if the instance don't add too many vertices for this VBHard
1269 if(allocator->exceedMaxVertexInBufferHard(shape->VB.getNumVertices()))
1271 // if exceed, then must pass ALL the IG in software mode. vertices/faces are correclty updated.
1272 // special: if rdrPass is the ZSort one,
1273 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
1275 nlassert(ig->_SortOwner->ZSortHardMode);
1277 // must do it on ALL igs of the sortBlock, for less VBuffer mode switching.
1278 CVegetableInstanceGroup *pIg= ig->_SortOwner->_InstanceGroupList.begin();
1279 while(pIg)
1281 // let's pass them in software mode.
1282 swapIgRdrPassHardMode(pIg, rdrPass);
1283 // next
1284 pIg= (CVegetableInstanceGroup*)pIg->Next;
1287 // Then all The sortBlock is in SoftMode.
1288 ig->_SortOwner->ZSortHardMode= false;
1290 else
1292 // just do it on this Ig (can mix hardMode in a SortBlock for normal rdrPass)
1293 swapIgRdrPassHardMode(ig, rdrPass);
1296 // now, we can use the software only Allocator to append our instance
1297 allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 0);
1302 // get correct dstVB
1303 const CVertexBuffer &dstVBInfo= allocator->getSoftwareVertexBuffer();
1306 // Transform vertices to a vegetable instance, and enlarge clipBlock
1307 //--------------------
1308 // compute matrix to multiply normals, ie (M-1)t
1309 CMatrix normalMat;
1310 // need just rotation scale matrix.
1311 normalMat.setRot(mat);
1312 normalMat.invert();
1313 normalMat.transpose();
1314 // compute Instance position
1315 CVector instancePos;
1316 mat.getPos(instancePos);
1319 // At least, the bbox of the clipBlock must include the center of the shape.
1320 ig->_ClipOwner->extendSphere(instancePos);
1323 // Vertex/triangle Info.
1324 uint numNewVertices= shape->VB.getNumVertices();
1325 uint numNewTris= (uint)shape->TriangleIndices.size()/3;
1326 uint numNewIndices= (uint)shape->TriangleIndices.size();
1328 // src info.
1329 uint srcNormalOff= (instanceLighted? shape->VB.getNormalOff() : 0);
1330 uint srcTex0Off= shape->VB.getTexCoordOff(0);
1331 uint srcTex1Off= shape->VB.getTexCoordOff(1);
1333 // dst info
1334 uint dstNormalOff= (destLighted? dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_NORMAL) : 0);
1335 uint dstColor0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR0);
1336 uint dstColor1Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR1);
1337 uint dstTex0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_TEX0);
1338 uint dstBendOff= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_BENDINFO);
1339 uint dstCenterOff= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_CENTER);
1341 // For D3D, If the VertexBuffer is in BGRA mode
1342 if(allocator->isBGRA())
1344 // then swap only the B and R (no cpu cycle added per vertex)
1345 primaryRGBA.swapBR();
1346 secondaryRGBA.swapBR();
1347 diffusePL[0].swapBR();
1348 diffusePL[1].swapBR();
1351 // Useful for !destLighted only.
1352 CVector deltaPos;
1353 float deltaPosNorm=0.0;
1356 // Useful for ZSORT rdrPass, the worldVertices.
1357 static vector<CVector> worldVertices;
1358 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
1360 worldVertices.resize(numNewVertices);
1363 CVertexBufferRead vba;
1364 shape->VB.lock (vba);
1366 // For all vertices of shape, transform and store manager indices in temp shape.
1367 for(i=0; i<(sint)numNewVertices;i++)
1369 // allocate a Vertex
1370 uint vid= allocator->allocateVertex();
1372 CVertexBufferReadWrite vbaOut;
1373 allocator->getSoftwareVertexBuffer ().lock(vbaOut);
1375 // store in tmp shape.
1376 shape->InstanceVertices[i]= vid;
1378 // Fill this vertex.
1379 const uint8 *srcPtr= (uint8*)vba.getVertexCoordPointer(i);
1380 uint8 *dstPtr= (uint8*)vbaOut.getVertexCoordPointer(vid);
1382 // Get bendWeight for this vertex.
1383 float vertexBendWeight= ((CUV*)(srcPtr + srcTex1Off))->U * bendFactor;
1385 // Pos.
1386 //-------
1387 // Separate Center and relative pos.
1388 CVector relPos= mat.mulVector(*(CVector*)srcPtr); // mulVector, because translation in v[center]
1389 // compute bendCenterPos
1390 CVector bendCenterPos;
1391 if(shape->BendCenterMode == CVegetableShapeBuild::BendCenterNull)
1392 bendCenterPos= CVector::Null;
1393 else
1395 CVector v= *(CVector*)srcPtr;
1396 v.z= 0;
1397 bendCenterPos= mat.mulVector(v); // mulVector, because translation in v[center]
1399 // copy
1400 deltaPos= relPos-bendCenterPos;
1401 *(CVector*)dstPtr= deltaPos;
1402 *(CVector*)(dstPtr + dstCenterOff)= instancePos + bendCenterPos;
1403 // if !destLighted, then VP is different
1404 if(!destLighted)
1406 deltaPosNorm= deltaPos.norm();
1407 // copy bendWeight in v.w
1408 CVectorH *vh= (CVectorH*)dstPtr;
1409 // Mul by deltaPosNorm, to draw an arc circle.
1410 vh->w= vertexBendWeight * deltaPosNorm;
1413 // Enlarge the clipBlock of the IG.
1414 // Since small shape, enlarge with each vertices. simpler and maybe faster.
1415 // TODO_VEGET: bend and clipping ...
1416 ig->_ClipOwner->extendBBoxOnly(instancePos + relPos);
1418 // prepare for ZSort
1419 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
1421 worldVertices[i]= instancePos + relPos;
1425 // Color-ligthing.
1426 //-------
1427 if(!precomputeLighting)
1429 // just copy the primary color (means diffuse part if lighted)
1430 *(CRGBA*)(dstPtr + dstColor0Off)= primaryRGBA;
1431 // normal and secondary color
1432 if(destLighted)
1434 // normal
1435 *(CVector*)(dstPtr + dstNormalOff)= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) );
1437 // If destLighted, secondaryRGBA is the ambient
1438 // else secondaryRGBA is used only for Alpha (DLM uv.v).
1439 *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA;
1441 else
1443 nlassert(!destLighted);
1445 // compute normal.
1446 CVector rotNormal= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) );
1447 // must normalize() because scale is possible.
1448 rotNormal.normalize();
1450 // Do the compute.
1451 if(!bestSidedPrecomputeLighting)
1453 computeVegetVertexLighting(rotNormal,
1454 _DirectionalLight, primaryRGBA, secondaryRGBA,
1455 vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) );
1457 else
1459 computeVegetVertexLightingForceBestSided(rotNormal,
1460 _DirectionalLight, primaryRGBA, secondaryRGBA,
1461 vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) );
1464 // copy secondaryRGBA, used only for Alpha (DLM uv.v).
1465 *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA;
1469 // Texture.
1470 //-------
1471 *(CUV*)(dstPtr + dstTex0Off)= *(CUV*)(srcPtr + srcTex0Off);
1473 // Bend.
1474 //-------
1475 CVector *dstBendPtr= (CVector*)(dstPtr + dstBendOff);
1476 // setup bend Phase.
1477 dstBendPtr->y= bendPhase;
1478 // setup bend Weight.
1479 // if !destLighted, then VP is different, vertexBendWeight is stored in v[0].w
1480 if(destLighted)
1481 dstBendPtr->x= vertexBendWeight;
1482 else
1483 // the VP need the norm of relPos in v[9].x
1484 dstBendPtr->x= deltaPosNorm;
1485 // setup bendFreqFactor
1486 dstBendPtr->z= bendFreqFactor;
1487 /// If AlphaBlend / ZSort rdrPass, then setup AlphaBlend computing.
1488 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
1490 // get ptr on v[9].w NB: in Unlit mode, it has 4 components.
1491 CVectorH *dstBendPtr= (CVectorH*)(dstPtr + dstBendOff);
1492 // setup the constant of linear formula:
1493 // Alpha= -1/blendTransDist * dist + blendDistMax/blendTransDist
1494 dstBendPtr->w= blendDistMax/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST;
1498 // fill the vertex in AGP.
1499 //-------
1500 allocator->flushVertex(vid);
1504 // must recompute the sphere according to the bbox.
1505 ig->_ClipOwner->updateSphere();
1508 // If ZSort, compute Triangle Centers and Orders for quadrant
1509 //--------------------
1510 if(rdrPass==NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
1512 // inform the SB that it must be updated.
1513 ig->_SortOwner->_Dirty= true;
1514 // For deletion, inform the ig that it has instances which impact the SB.
1515 ig->_HasZSortPassInstances= true;
1517 // change UnderWater falg of the SB
1518 if(vegetWaterState == AboveWater)
1519 ig->_SortOwner->_UnderWater= false;
1520 else if(vegetWaterState == UnderWater)
1521 ig->_SortOwner->_UnderWater= true;
1523 // static to avoid reallocation
1524 static vector<CVector> triangleCenters;
1525 triangleCenters.resize(numNewTris);
1527 // compute triangle centers
1528 for(uint i=0; i<numNewTris; i++)
1530 // get index in shape.
1531 uint v0= shape->TriangleIndices[i*3+0];
1532 uint v1= shape->TriangleIndices[i*3+1];
1533 uint v2= shape->TriangleIndices[i*3+2];
1535 // get world coord.
1536 const CVector &vert0= worldVertices[v0];
1537 const CVector &vert1= worldVertices[v1];
1538 const CVector &vert2= worldVertices[v2];
1540 // compute center
1541 triangleCenters[i]= (vert0 + vert1 + vert2) / 3;
1542 // relative to center of the sortBlock (for sint16 compression)
1543 triangleCenters[i]-= ig->_SortOwner->_Center;
1547 // resize the array. Actually only modify the number of triangles really setuped.
1548 uint offTri= ig->_TriangleQuadrantOrderNumTriangles;
1549 ig->_TriangleQuadrantOrderNumTriangles+= numNewTris;
1550 // verify user has correclty used reserveIg system.
1551 nlassert(ig->_TriangleQuadrantOrderNumTriangles * NL3D_VEGETABLE_NUM_QUADRANT <= ig->_TriangleQuadrantOrderArray.size());
1554 // compute distance for each quadrant. Since we are not sure of the sortBlockSize, mul with a (big: 16) security.
1555 // NB: for landscape practical usage, this left us with more than 1mm precision.
1556 float distFactor=32768/(16*ig->_SortOwner->_Radius);
1557 for(uint quadId=0; quadId<NL3D_VEGETABLE_NUM_QUADRANT; quadId++)
1559 const CVector &quadDir= CVegetableQuadrant::Dirs[quadId];
1561 // For all tris.
1562 for(uint i=0; i<numNewTris; i++)
1564 // compute the distance with orientation of the quadrant. (DotProduct)
1565 float dist= triangleCenters[i] * quadDir;
1566 // compress to sint16.
1567 ig->_TriangleQuadrantOrders[quadId][offTri + i]= (sint16)NLMISC::OptFastFloor(dist*distFactor);
1573 // Append list of indices and list of triangles to the IG
1574 //--------------------
1576 // TODO_VEGET_OPTIM: system reallocation of array is very bad...
1579 // compute dest start idx.
1580 uint offVertex= vegetRdrPass.NVertices;
1581 uint offTri= vegetRdrPass.NTriangles;
1582 uint offTriIdx= offTri*3;
1584 // verify user has correclty used reserveIg system.
1585 nlassert(offVertex + numNewVertices <= vegetRdrPass.Vertices.size());
1586 nlassert(offTriIdx + numNewIndices <= vegetRdrPass.TriangleIndices.getNumIndexes());
1587 nlassert(offTriIdx + numNewIndices <= vegetRdrPass.TriangleLocalIndices.size());
1590 // insert list of vertices to delete in ig vertices.
1591 vegetRdrPass.Vertices.copy(offVertex, offVertex+numNewVertices, &shape->InstanceVertices[0]);
1593 // insert array of triangles in ig.
1594 // for all indices, fill IG
1595 CIndexBufferReadWrite ibaWrite;
1596 vegetRdrPass.TriangleIndices.lock (ibaWrite);
1597 if (vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices16)
1599 uint16 *ptr = (uint16 *) ibaWrite.getPtr();
1600 for(i=0; i<(sint)numNewIndices; i++)
1602 // get the index of the vertex in the shape
1603 uint vid= shape->TriangleIndices[i];
1604 // re-direction, using InstanceVertices;
1605 #ifdef NL_DEBUG
1606 nlassert(shape->InstanceVertices[vid] <= 0xffff);
1607 #endif
1608 ptr[offTriIdx + i]= (uint16) shape->InstanceVertices[vid];
1609 // local re-direction: adding vertexOffset.
1610 vegetRdrPass.TriangleLocalIndices[offTriIdx + i]= offVertex + vid;
1613 else
1615 uint32 *ptr = (uint32 *) ibaWrite.getPtr();
1616 for(i=0; i<(sint)numNewIndices; i++)
1618 // get the index of the vertex in the shape
1619 uint vid= shape->TriangleIndices[i];
1620 // re-direction, using InstanceVertices;
1621 ptr[offTriIdx + i]= shape->InstanceVertices[vid];
1622 // local re-direction: adding vertexOffset.
1623 vegetRdrPass.TriangleLocalIndices[offTriIdx + i]= offVertex + vid;
1627 // new triangle and vertex size.
1628 vegetRdrPass.NTriangles+= numNewTris;
1629 vegetRdrPass.NVertices+= numNewVertices;
1632 // if lighted, must add a lightedInstance for lighting update.
1633 //--------------------
1634 if(instanceLighted)
1636 // first, update Ig.
1637 ig->_ULNumVertices+= numNewVertices;
1638 // and update the vegetable manager.
1639 _ULNTotalVertices+= numNewVertices;
1640 // link at the end of the circular list: link before the current root.
1641 if(_ULRootIg==NULL)
1642 _ULRootIg= ig;
1643 else
1644 ig->linkBeforeUL(_ULRootIg);
1646 // check good use of reserveIg.
1647 nlassert(vegetRdrPass.NLightedInstances < vegetRdrPass.LightedInstances.size());
1649 // Fill instance info
1650 CVegetableInstanceGroup::CVegetableLightedInstance &vli=
1651 vegetRdrPass.LightedInstances[vegetRdrPass.NLightedInstances];
1652 vli.Shape= shape;
1653 vli.NormalMat= normalMat;
1654 // copy colors unmodulated by global light.
1655 vli.MatAmbient= ambientRGBA;
1656 vli.MatDiffuse= diffuseRGBA;
1657 // store dynamic lightmap UV
1658 vli.DlmUV= dlmUV;
1659 // where vertices of this instances are wrote in the VegetRdrPass
1660 vli.StartIdInRdrPass= offVertex;
1662 // Inc size setuped.
1663 vegetRdrPass.NLightedInstances++;
1669 // ***************************************************************************
1670 void CVegetableManager::swapIgRdrPassHardMode(CVegetableInstanceGroup *ig, uint rdrPass)
1672 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass];
1674 // the allocator where vertices come from
1675 CVegetableVBAllocator &srcAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vegetRdrPass.HardMode);
1676 // the allocator where vertices will go
1677 CVegetableVBAllocator &dstAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, !vegetRdrPass.HardMode);
1679 // vertex size
1680 uint vbSize= srcAllocator.getSoftwareVertexBuffer().getVertexSize();
1681 nlassert(vbSize == dstAllocator.getSoftwareVertexBuffer().getVertexSize());
1683 CVertexBufferRead vbaIn;
1684 srcAllocator.getSoftwareVertexBuffer ().lock(vbaIn);
1686 // for all vertices of the IG, change of VBAllocator
1687 uint i;
1688 // Do it only for current Vertices setuped!!! because a swapIgRdrPassHardMode awlays arise when the ig is
1689 // in construcion.
1690 // Hence here, we may have vegetRdrPass.NVertices < vegetRdrPass.Vertices.size() !!!
1691 for(i=0;i<vegetRdrPass.NVertices;i++)
1693 // get idx in src allocator.
1694 uint srcId= vegetRdrPass.Vertices[i];
1695 // allocate a vertex in the dst allocator.
1696 uint dstId= dstAllocator.allocateVertex();
1698 CVertexBufferReadWrite vbaOut;
1699 dstAllocator.getSoftwareVertexBuffer ().lock(vbaOut);
1701 // copy from VBsoft of src to dst.
1702 const void *vbSrc= vbaIn.getVertexCoordPointer(srcId);
1703 void *vbDst= vbaOut.getVertexCoordPointer(dstId);
1704 memcpy(vbDst, vbSrc, vbSize);
1705 // release src vertex.
1706 srcAllocator.deleteVertex(srcId);
1708 // and copy new dest id in Vertices array.
1709 vegetRdrPass.Vertices[i]= dstId;
1711 // and flush this vertex into VBHard (if dst is aVBHard).
1712 dstAllocator.flushVertex(dstId);
1715 // For all triangles, bind correct triangles.
1716 nlassert(vegetRdrPass.TriangleIndices.getNumIndexes() == vegetRdrPass.TriangleLocalIndices.size());
1717 // Do it only for current Triangles setuped!!! same reason as vertices
1718 // For all setuped triangles indices
1719 CIndexBufferReadWrite ibaWrite;
1720 // For hard mode, uses faster 16 bit indices because the VB is not bigger than 65K
1721 vegetRdrPass.TriangleIndices.setFormat(vegetRdrPass.HardMode ? CIndexBuffer::Indices32 : CIndexBuffer::Indices16); // NB : this is not an error here : vegetRdrPass.HardMode has not been inverted yet
1722 vegetRdrPass.TriangleIndices.lock (ibaWrite);
1723 if (ibaWrite.getFormat() == CIndexBuffer::Indices16)
1725 uint16 *ptr = (uint16 *) ibaWrite.getPtr();
1726 for(i=0;i<vegetRdrPass.NTriangles*3;i++)
1728 // get the index in Vertices.
1729 uint localVid= vegetRdrPass.TriangleLocalIndices[i];
1730 // get the index in new VBufffer (dstAllocator), and copy to TriangleIndices
1731 ptr[i]= (uint16) vegetRdrPass.Vertices[localVid];
1734 else
1736 uint32 *ptr = (uint32 *) ibaWrite.getPtr();
1737 for(i=0;i<vegetRdrPass.NTriangles*3;i++)
1739 // get the index in Vertices.
1740 uint localVid= vegetRdrPass.TriangleLocalIndices[i];
1741 // get the index in new VBufffer (dstAllocator), and copy to TriangleIndices
1742 ptr[i]= (uint32) vegetRdrPass.Vertices[localVid];
1746 // Since change is made, flag the IG rdrpass
1747 vegetRdrPass.HardMode= !vegetRdrPass.HardMode;
1751 // ***************************************************************************
1752 void CVegetableManager::setGlobalDensity(float density)
1754 clamp(density, 0.f, 100.f);
1755 _GlobalDensity= density;
1759 // ***************************************************************************
1760 // ***************************************************************************
1761 // Render
1762 // ***************************************************************************
1763 // ***************************************************************************
1766 // ***************************************************************************
1767 bool CVegetableManager::doubleSidedRdrPass(uint rdrPass)
1769 nlassert(rdrPass<NL3D_VEGETABLE_NRDRPASS);
1770 return (rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED) ||
1771 (rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED) ||
1772 (rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT);
1775 // ***************************************************************************
1776 void CVegetableManager::updateDriver(IDriver *driver)
1778 // update all driver
1779 uint i;
1780 for(i=0; i <CVegetableVBAllocator::VBTypeCount; i++)
1782 _VBHardAllocator[i].updateDriver(driver);
1783 _VBSoftAllocator[i].updateDriver(driver);
1786 // if driver changed, recreate vertex programs
1787 if (driver != _LastDriver)
1789 _LastDriver = driver;
1790 for(i=0; i <NL3D_VEGETABLE_NRDRPASS; i++)
1792 // both fog & no fog
1793 initVertexProgram(i, true);
1794 initVertexProgram(i, false);
1800 // ***************************************************************************
1801 void CVegetableManager::loadTexture(const string &texName)
1803 // setup a CTextureFile (smartPtr-ized).
1804 ITexture *tex= new CTextureFile(texName);
1805 loadTexture(tex);
1806 // setup good params.
1807 tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear);
1808 tex->setWrapS(ITexture::Clamp);
1809 tex->setWrapT(ITexture::Clamp);
1812 // ***************************************************************************
1813 void CVegetableManager::loadTexture(ITexture *itex)
1815 // setup a ITexture (smartPtr-ized).
1816 // Store in stage1, for dynamicLightmaping
1817 _VegetableMaterial.setTexture(1, itex);
1820 // ***************************************************************************
1821 void CVegetableManager::setDirectionalLight(const CRGBA &ambient, const CRGBA &diffuse, const CVector &light)
1823 _DirectionalLight= light;
1824 _DirectionalLight.normalize();
1825 // Setup ambient/Diffuse.
1826 _GlobalAmbient= ambient;
1827 _GlobalDiffuse= diffuse;
1830 // ***************************************************************************
1831 void CVegetableManager::lockBuffers()
1833 // lock all buffers
1834 for(uint i=0; i <CVegetableVBAllocator::VBTypeCount; i++)
1836 _VBHardAllocator[i].lockBuffer();
1837 _VBSoftAllocator[i].lockBuffer();
1841 // ***************************************************************************
1842 void CVegetableManager::unlockBuffers()
1844 // unlock all buffers
1845 for(uint i=0; i <CVegetableVBAllocator::VBTypeCount; i++)
1847 _VBHardAllocator[i].unlockBuffer();
1848 _VBSoftAllocator[i].unlockBuffer();
1853 // ***************************************************************************
1854 class CSortVSB
1856 public:
1857 CVegetableSortBlock *Sb;
1859 CSortVSB() : Sb(NULL) {}
1860 CSortVSB(CVegetableSortBlock *sb) : Sb(sb) {}
1863 // for sort()
1864 bool operator<(const CSortVSB &o) const
1866 return Sb->_SortKey>o.Sb->_SortKey;
1872 // ***************************************************************************
1873 void CVegetableManager::setupVertexProgramConstants(IDriver *driver, bool fogEnabled)
1875 nlassert(_ActiveVertexProgram);
1878 // Standard
1879 // setup VertexProgram constants.
1880 // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix();
1881 driver->setUniformMatrix(IDriver::VertexProgram, _ActiveVertexProgram->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity);
1882 // c[6] take the Fog vector. After setupModelMatrix();
1883 if (fogEnabled)
1885 driver->setUniformFog(IDriver::VertexProgram, _ActiveVertexProgram->getUniformIndex(CProgramIndex::Fog));
1887 // c[8] take useful constants.
1888 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().ProgramConstants0, 0, 1, 0.5f, 2);
1889 // c[9] take normalized directional light
1890 driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().DirectionalLight, _DirectionalLight);
1891 // c[10] take pos of camera
1892 driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().ViewCenter, _ViewCenter);
1893 // c[11] take factor for Blend formula
1894 driver->setUniform1f(IDriver::VertexProgram, _ActiveVertexProgram->idx().NegInvTransDist, -1.f/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST);
1898 // Bend.
1899 // c[16]= quaternion axis. w==1, and z must be 0
1900 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().AngleAxis, _AngleAxis, 1);
1901 // c[17]= {timeAnim, WindPower, WindPower*(1-WindBendMin)/2, 0)}
1902 driver->setUniform3f(IDriver::VertexProgram, _ActiveVertexProgram->idx().Wind, (float)_WindAnimTime, _WindPower, _WindPower * (1 - _WindBendMin) / 2);
1903 // c[18]= High order Taylor cos coefficient: { -1/2, 1/24, -1/720, 1/40320 }
1904 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff0, -1/2.f, 1/24.f, -1/720.f, 1/40320.f );
1905 // c[19]= Low order Taylor cos coefficient: { 1, -1/2, 1/24, -1/720 }
1906 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff1, 1, -1/2.f, 1/24.f, -1/720.f );
1907 // c[20]= Low order Taylor sin coefficient: { 1, -1/6, 1/120, -1/5040 }
1908 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().CosCoeff2, 1, -1/6.f, 1/120.f, -1/5040.f );
1909 // c[21]= Special constant vector for quatToMatrix: { 0, 1, -1, 0 }
1910 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().QuatConstants, 0.f, 1.f, -1.f, 0.f);
1911 // c[22]= {0.5f, Pi, 2*Pi, 1/(2*Pi)}
1912 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().PiConstants, 0.5f, (float)Pi, (float)(2*Pi), (float)(1/(2*Pi)));
1913 // c[23]= {NL3D_VEGETABLE_VP_LUT_SIZE, 0, 0, 0}. NL3D_VEGETABLE_VP_LUT_SIZE==64.
1914 driver->setUniform1f(IDriver::VertexProgram, _ActiveVertexProgram->idx().LUTSize, NL3D_VEGETABLE_VP_LUT_SIZE);
1917 // Fill constant. Start at 32.
1918 for(uint i=0; i<NL3D_VEGETABLE_VP_LUT_SIZE; i++)
1920 CVector2f cur= _WindTable[i];
1921 CVector2f delta= _WindDeltaTable[i];
1922 driver->setUniform4f(IDriver::VertexProgram, _ActiveVertexProgram->idx().LUT[i], cur.x, cur.y, delta.x, delta.y);
1927 // ***************************************************************************
1928 void CVegetableManager::render(const CVector &viewCenter, const CVector &frontVector, const std::vector<CPlane> &pyramid,
1929 ITexture *textureDLM, IDriver *driver)
1931 H_AUTO( NL3D_Vegetable_Render );
1933 CVegetableClipBlock *rootToRender= NULL;
1935 // get normalized front vector.
1936 CVector frontVectorNormed= frontVector.normed();
1938 // For Speed debug only.
1939 /*extern bool YOYO_ATTest;
1940 if(YOYO_ATTest)
1941 return;
1944 // Clip.
1945 //--------------------
1946 // For all current not empty clipBlocks, clip against pyramid, and insert visibles in list.
1947 CVegetableClipBlock *ptrClipBlock= _ClipBlockList.begin();
1948 while(ptrClipBlock)
1950 // if the clipBlock is visible and not empty
1951 if(ptrClipBlock->clip(pyramid))
1953 // insert into visible list.
1954 ptrClipBlock->_RenderNext= rootToRender;
1955 rootToRender= ptrClipBlock;
1958 // next
1959 ptrClipBlock= (CVegetableClipBlock*)ptrClipBlock->Next;
1963 // If no clip block visible, just skip!!
1964 if(rootToRender==NULL)
1965 return;
1968 // Prepare Render
1969 //--------------------
1971 // profile.
1972 CPrimitiveProfile ppIn, ppOut;
1973 driver->profileRenderedPrimitives(ppIn, ppOut);
1974 uint precNTriRdr= ppOut.NTriangles;
1977 // Disable Fog.
1978 bool bkupFog;
1979 bkupFog= driver->fogEnabled();
1981 bool fogged = bkupFog && driver->getFogStart() < _ZSortLayerDistMax;
1984 driver->enableFog(fogged);
1987 // Used by setupVertexProgramConstants(). The center of camera.
1988 // Used for AlphaBlending, and for ZBuffer precision problems.
1989 _ViewCenter= viewCenter;
1992 // The manager is identity in essence. But for ZBuffer improvements, must set it as close
1993 // to the camera. In the VertexProgram, _ViewCenter is substracted from bent vertex pos. So take it as position.
1994 _ManagerMatrix.identity();
1995 _ManagerMatrix.setPos(_ViewCenter);
1998 // set model matrix to the manager matrix.
1999 driver->setupModelMatrix(_ManagerMatrix);
2002 // set the driver for all allocators
2003 updateDriver(driver);
2006 // Compute Bend Anim.
2008 // AnimFrequency factor.
2009 // Doing it incrementally allow change of of frequency each frame with good results.
2010 _WindAnimTime+= (_Time - _WindPrecRenderTime)*_WindFrequency;
2011 _WindAnimTime= fmod((float)_WindAnimTime, (float)NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC);
2012 // NB: Leave timeBend (_WindAnimTime) as a time (ie [0..1]), because VP do a "EXP time".
2013 // For incremental computing.
2014 _WindPrecRenderTime= _Time;
2017 // compute the angleAxis corresponding to direction
2018 // perform a 90deg rotation to get correct angleAxis
2019 _AngleAxis.set(-_WindDirection.y,_WindDirection.x,0);
2022 // Fill LUT WindTable.
2023 uint i;
2024 for(i=0; i<NL3D_VEGETABLE_VP_LUT_SIZE; i++)
2026 /* NB: this formula works quite well, because vertex BendFactor is expressed in Radian/2.
2027 And since animFactor==(_CosTable[i] + 1) E [0..2], we have here an arc-circle computing:
2028 dmove= Radius * AngleRadian/2 * animFactor. So at max of animFactor (ie 2), we have:
2029 dmove= Radius * AngleRadian, which is by definition an arc-circle computing...
2030 And so this approximate the Bend-quaternion Vertex Program.
2032 float windForce= (_CosTable[(i+32)%64] + 1);
2033 // Modify with _WindPower / _WindBendMin.
2034 windForce= _WindBendMin*2 + windForce * (1-_WindBendMin);
2035 windForce*= _WindPower;
2036 // Compute direction of the wind, and multiply by windForce.
2037 _WindTable[i]= CVector2f(_WindDirection.x, _WindDirection.y) * windForce;
2039 // compute delta
2040 for(i=0; i<NL3D_VEGETABLE_VP_LUT_SIZE; i++)
2042 CVector2f cur= _WindTable[i];
2043 CVector2f delta= _WindTable[ (i+1)%NL3D_VEGETABLE_VP_LUT_SIZE ] - cur;
2044 _WindDeltaTable[i]= delta;
2048 // Setup TexEnvs for Dynamic lightmapping
2049 //--------------------
2050 // if the dynamic lightmap is provided
2051 if(textureDLM)
2053 // stage0 RGB is Diffuse + DLM.
2054 _VegetableMaterial.setTexture(0, textureDLM);
2055 _VegetableMaterial.texEnvOpRGB(0, CMaterial::Add);
2056 _VegetableMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
2057 _VegetableMaterial.texEnvArg1RGB(0, CMaterial::Diffuse, CMaterial::SrcColor);
2058 // stage1 RGB is Previous * Texture
2059 _VegetableMaterial.texEnvOpRGB(1, CMaterial::Modulate);
2060 _VegetableMaterial.texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor);
2061 _VegetableMaterial.texEnvArg1RGB(1, CMaterial::Previous, CMaterial::SrcColor);
2063 else
2065 // reset stage0 (to skip it)
2066 _VegetableMaterial.setTexture(0, NULL);
2067 // stage1 RGB is Diffuse * Texture
2068 _VegetableMaterial.texEnvOpRGB(1, CMaterial::Modulate);
2069 _VegetableMaterial.texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor);
2070 _VegetableMaterial.texEnvArg1RGB(1, CMaterial::Diffuse, CMaterial::SrcColor);
2072 // stage1 Alpha is always "Modulate texture with diffuse Alpha"
2073 _VegetableMaterial.texEnvOpAlpha(1, CMaterial::Modulate);
2074 _VegetableMaterial.texEnvArg0Alpha(1, CMaterial::Texture, CMaterial::SrcAlpha);
2075 _VegetableMaterial.texEnvArg1Alpha(1, CMaterial::Diffuse, CMaterial::SrcAlpha);
2079 // Render !ZSORT pass
2080 //--------------------
2082 // setup material (may have change because of ZSORT / alphaBlend pass)
2083 _VegetableMaterial.setBlend(false);
2084 _VegetableMaterial.setZWrite(true);
2085 _VegetableMaterial.setAlphaTestThreshold(0.5f);
2087 bool uprogst = driver->isUniformProgramState();
2088 bool progstateset[NL3D_VEGETABLE_NRDRPASS];
2089 for (sint rdrPass = 0; rdrPass < NL3D_VEGETABLE_NRDRPASS; ++rdrPass)
2091 progstateset[rdrPass] = false;
2095 Prefer sort with Soft / Hard first.
2096 Also, Prefer do VBsoft last, for better GPU //ism with Landscape.
2098 // For both allocators: Hard(1) then Soft(0)
2099 for(sint vbHardMode= 1; vbHardMode>=0; vbHardMode--)
2101 // For all renderPass.
2102 for(sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++)
2104 // skip ZSORT rdrPass, done after.
2105 if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT)
2106 continue;
2108 // which allocator?
2109 CVegetableVBAllocator &vbAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vbHardMode);
2112 // Do the pass only if there is some vertices to draw.
2113 if(vbAllocator.getNumUserVerticesAllocated()>0)
2115 // additional setup to the material
2116 bool doubleSided= doubleSidedRdrPass(rdrPass);
2117 // set the 2Sided flag in the material
2118 _VegetableMaterial.setDoubleSided( doubleSided );
2120 // activate Vertex program first.
2121 //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str());
2123 _ActiveVertexProgram = _VertexProgram[rdrPass][fogged ? 1 : 0];
2124 nlverify(driver->activeVertexProgram(_ActiveVertexProgram));
2126 // Set VP constants
2127 if (!progstateset[uprogst ? rdrPass : 0])
2129 setupVertexProgramConstants(driver, uprogst ? fogged : true);
2132 // Activate the unique material.
2133 driver->setupMaterial(_VegetableMaterial);
2135 // Activate the good VBuffer
2136 vbAllocator.activate();
2138 // For all visibles clipBlock, render their instance groups.
2139 ptrClipBlock= rootToRender;
2140 while(ptrClipBlock)
2142 // For all sortBlock of the clipBlock
2143 CVegetableSortBlock *ptrSortBlock= ptrClipBlock->_SortBlockList.begin();
2144 while(ptrSortBlock)
2146 // For all igs of the sortBlock
2147 CVegetableInstanceGroup *ptrIg= ptrSortBlock->_InstanceGroupList.begin();
2148 while(ptrIg)
2150 // rdrPass
2151 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ptrIg->_RdrPass[rdrPass];
2153 // if this rdrPass is in same HardMode as we process now.
2154 if( (vegetRdrPass.HardMode && vbHardMode==1) || (!vegetRdrPass.HardMode && vbHardMode==0) )
2156 // Ok, Render the faces.
2157 if(vegetRdrPass.NTriangles)
2159 driver->activeIndexBuffer(vegetRdrPass.TriangleIndices);
2160 #ifdef NL_DEBUG
2161 if (vegetRdrPass.HardMode)
2163 nlassert(vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices16);
2165 else
2167 nlassert(vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices32);
2169 #endif
2170 driver->renderSimpleTriangles(0,
2171 vegetRdrPass.NTriangles);
2175 // next ig.
2176 ptrIg= (CVegetableInstanceGroup*)ptrIg->Next;
2179 // next sortBlock
2180 ptrSortBlock= (CVegetableSortBlock *)(ptrSortBlock->Next);
2183 // next clipBlock to render
2184 ptrClipBlock= ptrClipBlock->_RenderNext;
2192 // Render ZSort pass.
2193 //--------------------
2195 // Debug Quadrants.
2196 /*static vector<CVector> p0DebugLines;
2197 static vector<CVector> p1DebugLines;
2198 p0DebugLines.clear();
2199 p1DebugLines.clear();*/
2201 // For all Blend model Layers, clear Sort Block list and setup.
2202 for(i=0; i<_NumZSortBlendLayers;i++)
2204 // must have been created.
2205 nlassert(_ZSortModelLayers[i]);
2206 nlassert(_ZSortModelLayersUW[i]);
2207 // NB: don't refresh list, it is done in CVegetableBlendLayerModel.
2208 // We must do it here, because if vegetableManger::render() is no more called (eg: disabled),
2209 // then the models must do nothing.
2211 // To get layers correclty sorted from fornt to back, must init their pos
2212 // because it is the renderTraversal which sort them.
2213 // compute distance to camera of this layer.
2214 float layerZ= i * _ZSortLayerDistMax / _NumZSortBlendLayers;
2215 // compute position of this layer.
2216 CVector pos= viewCenter + frontVector * layerZ;
2217 // special setup in the layer.
2218 _ZSortModelLayers[i]->setWorldPos(pos);
2219 _ZSortModelLayersUW[i]->setWorldPos(pos);
2222 // If some vertices in arrays for ZSort rdrPass
2223 if( getVBAllocatorForRdrPassAndVBHardMode(NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT, 0).getNumUserVerticesAllocated()>0 ||
2224 getVBAllocatorForRdrPassAndVBHardMode(NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT, 1).getNumUserVerticesAllocated()>0 )
2226 uint rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT;
2228 // sort
2229 //-------------
2230 // Array for sorting. (static to avoid reallocation)
2231 static vector<CSortVSB> sortVegetSbs;
2232 sortVegetSbs.clear();
2234 // For all visibles clipBlock
2235 ptrClipBlock= rootToRender;
2236 while(ptrClipBlock)
2238 // For all sortBlock, prepare to sort them
2239 CVegetableSortBlock *ptrSortBlock= ptrClipBlock->_SortBlockList.begin();
2240 while(ptrSortBlock)
2242 // if the sortBlock has some sorted faces to render
2243 if(ptrSortBlock->_NTriangles != 0)
2245 // Compute Distance to Viewer.
2246 /* NB: compute radial distance (with norm()) instead of linear distance
2247 (DotProduct with front vector) get less "ZSort poping".
2249 CVector dirToSb= ptrSortBlock->_Center - viewCenter;
2250 float distToViewer= dirToSb.norm();
2251 // SortKey change if the center is behind the camera.
2252 if(dirToSb * frontVectorNormed<0)
2254 ptrSortBlock->_SortKey= - distToViewer;
2256 else
2258 ptrSortBlock->_SortKey= distToViewer;
2261 // Choose the quadrant for this sortBlock
2262 sint bestDirIdx= 0;
2263 float bestDirVal= -FLT_MAX;
2264 // If too near, must take the frontVector as key, to get better sort.
2265 // use ptrSortBlock->_SortKey to get correct negative values.
2266 if(ptrSortBlock->_SortKey < ptrSortBlock->_Radius)
2268 dirToSb= frontVectorNormed;
2271 // NB: no need to normalize dirToSb, because need only to sort with DP
2272 // choose the good list of triangles according to quadrant.
2273 for(uint dirIdx=0; dirIdx<NL3D_VEGETABLE_NUM_QUADRANT; dirIdx++)
2275 float dirVal= CVegetableQuadrant::Dirs[dirIdx] * dirToSb;
2276 if(dirVal>bestDirVal)
2278 bestDirVal= dirVal;
2279 bestDirIdx= dirIdx;
2283 // set the result.
2284 ptrSortBlock->_QuadrantId= bestDirIdx;
2286 // insert in list to sort.
2287 sortVegetSbs.push_back(CSortVSB(ptrSortBlock));
2289 // Debug Quadrants
2290 /*p0DebugLines.push_back(ptrSortBlock->_Center);
2291 p1DebugLines.push_back(ptrSortBlock->_Center + CVegetableQuadrant::Dirs[bestDirIdx]);*/
2294 // next sortBlock
2295 ptrSortBlock= (CVegetableSortBlock *)(ptrSortBlock->Next);
2298 // next clipBlock to render
2299 ptrClipBlock= ptrClipBlock->_RenderNext;
2302 // sort!
2303 // QSort. (I tried, better than radix sort, guckk!!)
2304 sort(sortVegetSbs.begin(), sortVegetSbs.end());
2307 // setup material for this rdrPass. NB: rendered after (in LayerModels).
2308 //-------------
2309 bool doubleSided= doubleSidedRdrPass(rdrPass);
2310 // set the 2Sided flag in the material
2311 _VegetableMaterial.setDoubleSided( doubleSided );
2313 // setup the unique material.
2314 _VegetableMaterial.setBlend(true);
2315 _VegetableMaterial.setZWrite(false);
2316 // leave AlphaTest but still kick low alpha values (for fillRate performance)
2317 _VegetableMaterial.setAlphaTestThreshold(0.1f);
2321 // order them in Layers.
2322 //-------------
2324 // render from back to front, to keep correct Z order in a single layer.
2325 for(uint i=0; i<sortVegetSbs.size();i++)
2327 CVegetableSortBlock *ptrSortBlock= sortVegetSbs[i].Sb;
2329 float z= ptrSortBlock->_SortKey;
2330 // compute in which layer must store this SB.
2331 z= z*_NumZSortBlendLayers / _ZSortLayerDistMax;
2332 // Avoid a floor(), using an OptFastFloor, but without the OptFastFloorBegin() End() group.
2333 // => avoid the imprecision with such a trick; *256, then divide the integer by 256.
2334 sint layer= NLMISC::OptFastFloor(z*256) >> 8;
2335 clamp(layer, 0, (sint)_NumZSortBlendLayers-1);
2337 // Range in correct layer, according to water ordering
2338 if(ptrSortBlock->_UnderWater)
2339 // range in the correct layermodel (NB: keep the same layer internal order).
2340 _ZSortModelLayersUW[layer]->SortBlocks.push_back(ptrSortBlock);
2341 else
2342 _ZSortModelLayers[layer]->SortBlocks.push_back(ptrSortBlock);
2348 // Quit
2349 //--------------------
2351 // disable VertexProgram.
2352 driver->activeVertexProgram(NULL);
2353 _ActiveVertexProgram = NULL;
2356 // restore Fog.
2357 driver->enableFog(bkupFog);
2360 // Debug Quadrants
2361 /*for(uint l=0; l<p0DebugLines.size();l++)
2363 CVector dv= CVector::K;
2364 CDRU::drawLine(p0DebugLines[l]+dv, p1DebugLines[l]+dv, CRGBA(255,0,0), *driver);
2367 // profile: compute number of triangles rendered with vegetable manager.
2368 driver->profileRenderedPrimitives(ppIn, ppOut);
2369 _NumVegetableFaceRendered= ppOut.NTriangles-precNTriRdr;
2378 // ***************************************************************************
2379 void CVegetableManager::setupRenderStateForBlendLayerModel(IDriver *driver)
2381 // Setup Global.
2382 //=============
2384 // disable fog, for faster VP.
2385 _BkupFog= driver->fogEnabled();
2386 static volatile bool testDist = true;
2387 bool fogged = _BkupFog && driver->getFogStart() < _ZSortLayerDistMax;
2388 driver->enableFog(fogged);
2390 // set model matrix to the manager matrix.
2391 driver->setupModelMatrix(_ManagerMatrix);
2393 // Setup RdrPass.
2394 //=============
2395 uint rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT;
2397 // activate Vertex program first.
2398 //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str());
2399 _ActiveVertexProgram = _VertexProgram[rdrPass][fogged ? 1 : 0];
2400 nlverify(driver->activeVertexProgram(_ActiveVertexProgram));
2402 // setup VP constants.
2403 setupVertexProgramConstants(driver, fogged);
2405 /*if (fogged) // duplicate
2407 driver->setCon/stantFog(6);
2410 // Activate the unique material (correclty setuped for AlphaBlend in render()).
2411 driver->setupMaterial(_VegetableMaterial);
2415 // ***************************************************************************
2416 void CVegetableManager::resetNumVegetableFaceRendered()
2418 _NumVegetableFaceRendered= 0;
2422 // ***************************************************************************
2423 uint CVegetableManager::getNumVegetableFaceRendered() const
2425 return _NumVegetableFaceRendered;
2429 // ***************************************************************************
2430 void CVegetableManager::exitRenderStateForBlendLayerModel(IDriver *driver)
2432 // disable VertexProgram.
2433 driver->activeVertexProgram(NULL);
2434 _ActiveVertexProgram = NULL;
2436 // restore Fog.
2437 driver->enableFog(_BkupFog);
2442 // ***************************************************************************
2443 void CVegetableManager::setWind(const CVector &windDir, float windFreq, float windPower, float windBendMin)
2445 // Keep only XY component of the Wind direction (because VP only support z==0 quaternions).
2446 _WindDirection= windDir;
2447 _WindDirection.z= 0;
2448 _WindDirection.normalize();
2449 // copy setup
2450 _WindFrequency= windFreq;
2451 _WindPower= windPower;
2452 _WindBendMin= windBendMin;
2453 clamp(_WindBendMin, 0, 1);
2456 // ***************************************************************************
2457 void CVegetableManager::setTime(double time)
2459 // copy time
2460 _Time= time;
2464 // ***************************************************************************
2465 // ***************************************************************************
2466 // Lighting part.
2467 // ***************************************************************************
2468 // ***************************************************************************
2471 // ***************************************************************************
2472 void CVegetableManager::setUpdateLightingTime(double time)
2474 _ULTime= time;
2478 // ***************************************************************************
2479 void CVegetableManager::updateLighting()
2481 // first time in this method??
2482 if(!_ULPrecTimeInit)
2484 _ULPrecTimeInit= true;
2485 _ULPrecTime= _ULTime;
2487 // compute delta time from last update.
2488 float dt= float(_ULTime - _ULPrecTime);
2489 _ULPrecTime= _ULTime;
2491 // compute number of vertices to update.
2492 _ULNVerticesToUpdate+= dt*_ULFrequency * _ULNTotalVertices;
2493 // maximize, so at max, it computes all Igs, just one time.
2494 _ULNVerticesToUpdate= min(_ULNVerticesToUpdate, (float)_ULNTotalVertices);
2496 // go.
2497 doUpdateLighting();
2501 // ***************************************************************************
2502 void CVegetableManager::updateLightingAll()
2504 // maximize, so at max, it computes all Igs
2505 _ULNVerticesToUpdate= (float)_ULNTotalVertices;
2507 // go.
2508 doUpdateLighting();
2512 // ***************************************************************************
2513 void CVegetableManager::doUpdateLighting()
2515 // while there is still some vertices to update.
2516 while(_ULNVerticesToUpdate > 0 && _ULRootIg)
2518 // update the current ig. if all updated, skip to next one.
2519 if(updateLightingIGPart())
2521 // next
2522 _ULRootIg= _ULRootIg->_ULNext;
2526 // Now, _ULNVerticesToUpdate should be <=0. (most of the time < 0)
2530 // ***************************************************************************
2531 void CVegetableManager::setUpdateLightingFrequency(float freq)
2533 freq= max(freq, 0.f);
2534 _ULFrequency= freq;
2538 // ***************************************************************************
2539 bool CVegetableManager::updateLightingIGPart()
2541 nlassert(_ULRootIg);
2544 // First, update lighting info global to the ig, ie update current
2545 // colros of the PointLights which influence the ig.
2546 _ULRootIg->VegetableLightEx.computeCurrentColors();
2548 // while there is some vertices to update
2549 while(_ULNVerticesToUpdate>0)
2551 // if all rdrPass of the ig are processed.
2552 if(_ULCurrentIgRdrPass>= NL3D_VEGETABLE_NRDRPASS)
2554 // All this Ig is updated.
2555 _ULCurrentIgRdrPass= 0;
2556 _ULCurrentIgInstance= 0;
2557 // skip to next Ig.
2558 return true;
2560 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= _ULRootIg->_RdrPass[_ULCurrentIgRdrPass];
2562 // if all instances are processed for this pass (especially if empty() !!)
2563 if(_ULCurrentIgInstance>= vegetRdrPass.LightedInstances.size())
2565 // skip to the next rdrPass.
2566 _ULCurrentIgRdrPass++;
2567 _ULCurrentIgInstance= 0;
2568 continue;
2571 // Process this instance.
2572 _ULNVerticesToUpdate-= updateInstanceLighting(_ULRootIg, _ULCurrentIgRdrPass, _ULCurrentIgInstance);
2574 // next instance.
2575 _ULCurrentIgInstance++;
2577 // if all instances are processed for this pass
2578 if(_ULCurrentIgInstance>= vegetRdrPass.LightedInstances.size())
2580 // skip to the next rdrPass.
2581 _ULCurrentIgRdrPass++;
2582 _ULCurrentIgInstance= 0;
2586 // If all rdrPass of the ig are processed.
2587 if(_ULCurrentIgRdrPass>= NL3D_VEGETABLE_NRDRPASS)
2589 // All this Ig is updated.
2590 _ULCurrentIgRdrPass= 0;
2591 _ULCurrentIgInstance= 0;
2592 // skip to next Ig.
2593 return true;
2595 else
2597 // The Ig is not entirely updated.
2598 return false;
2604 // ***************************************************************************
2605 uint CVegetableManager::updateInstanceLighting(CVegetableInstanceGroup *ig, uint rdrPassId, uint instanceId)
2607 nlassert(ig);
2608 // get the rdrPass.
2609 nlassert(rdrPassId<NL3D_VEGETABLE_NRDRPASS);
2610 CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPassId];
2611 // get the lighted instance.
2612 nlassert(instanceId<vegetRdrPass.LightedInstances.size());
2613 CVegetableInstanceGroup::CVegetableLightedInstance &vegetLI= vegetRdrPass.LightedInstances[instanceId];
2615 // get the shape
2616 CVegetableShape *shape= vegetLI.Shape;
2617 // it must be lighted.
2618 nlassert(shape->Lighted);
2619 bool instanceLighted= true;
2622 // get ref on the vegetLex.
2623 CVegetableLightEx &vegetLex= ig->VegetableLightEx;
2624 // Color of pointLights modulated by diffuse.
2625 CRGBA diffusePL[2];
2626 diffusePL[0] = CRGBA::Black;
2627 diffusePL[1] = CRGBA::Black;
2628 if(vegetLex.NumLights>=1)
2630 diffusePL[0].modulateFromColorRGBOnly(vegetLI.MatDiffuse, vegetLex.Color[0]);
2631 if(vegetLex.NumLights>=2)
2633 diffusePL[1].modulateFromColorRGBOnly(vegetLI.MatDiffuse, vegetLex.Color[1]);
2637 // Recompute lighting
2638 //===========
2640 // setup for this instance.
2641 //---------
2642 // Precompute lighting or not??
2643 bool precomputeLighting= instanceLighted && shape->PreComputeLighting;
2644 // bestSided Precompute lighting or not??
2645 bool bestSidedPrecomputeLighting= precomputeLighting && shape->BestSidedPreComputeLighting;
2646 // destLighted?
2647 bool destLighted= instanceLighted && !shape->PreComputeLighting;
2648 // Diffuse and ambient, modulated by current GlobalAmbient and GlobalDiffuse.
2649 CRGBA primaryRGBA, secondaryRGBA;
2650 primaryRGBA.modulateFromColorRGBOnly(vegetLI.MatDiffuse, _GlobalDiffuse);
2651 secondaryRGBA.modulateFromColorRGBOnly(vegetLI.MatAmbient, _GlobalAmbient);
2652 // get normal matrix
2653 CMatrix &normalMat= vegetLI.NormalMat;
2654 // array of vertex id to update
2655 uint32 *ptrVid= vegetRdrPass.Vertices.getPtr() + vegetLI.StartIdInRdrPass;
2656 uint numVertices= (uint)shape->InstanceVertices.size();
2658 // Copy Dynamic Lightmap UV in Alpha part (save memory for an extra cost of 1 VP instruction)
2659 primaryRGBA.A= vegetLI.DlmUV.U;
2660 secondaryRGBA.A= vegetLI.DlmUV.V;
2663 // get VertexBuffer info.
2664 CVegetableVBAllocator *allocator;
2665 allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPassId, vegetRdrPass.HardMode);
2666 const CVertexBuffer &dstVBInfo= allocator->getSoftwareVertexBuffer();
2668 uint srcNormalOff= (instanceLighted? shape->VB.getNormalOff() : 0);
2669 uint dstColor0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR0);
2670 uint dstColor1Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR1);
2672 // For D3D, If the VertexBuffer is in BGRA mode
2673 if(allocator->isBGRA())
2675 // then swap only the B and R (no cpu cycle added per vertex)
2676 primaryRGBA.swapBR();
2677 secondaryRGBA.swapBR();
2678 diffusePL[0].swapBR();
2679 diffusePL[1].swapBR();
2682 CVertexBufferRead vba;
2683 shape->VB.lock (vba);
2684 CVertexBufferReadWrite vbaOut;
2685 allocator->getSoftwareVertexBuffer ().lock(vbaOut);
2687 // For all vertices, recompute lighting.
2688 //---------
2689 for(sint i=0; i<(sint)numVertices;i++)
2691 // get the Vertex in the VB.
2692 uint vid= ptrVid[i];
2693 // store in tmp shape.
2694 shape->InstanceVertices[i]= vid;
2696 // Fill this vertex.
2697 const uint8 *srcPtr= (const uint8*)vba.getVertexCoordPointer(i);
2698 uint8 *dstPtr= (uint8*)vbaOut.getVertexCoordPointer(vid);
2701 // if !precomputeLighting (means destLighted...)
2702 if(!precomputeLighting)
2704 // just copy the primary and secondary color
2705 *(CRGBA*)(dstPtr + dstColor0Off)= primaryRGBA;
2706 *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA;
2708 else
2710 nlassert(!destLighted);
2712 // compute normal.
2713 CVector rotNormal= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) );
2714 // must normalize() because scale is possible.
2715 rotNormal.normalize();
2717 // Do the compute.
2718 if(!bestSidedPrecomputeLighting)
2720 computeVegetVertexLighting(rotNormal,
2721 _DirectionalLight, primaryRGBA, secondaryRGBA,
2722 vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) );
2724 else
2726 computeVegetVertexLightingForceBestSided(rotNormal,
2727 _DirectionalLight, primaryRGBA, secondaryRGBA,
2728 vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) );
2733 // flust the vertex in AGP.
2734 allocator->flushVertex(vid);
2738 // numVertices vertices are updated
2739 return numVertices;
2743 } // NL3D