// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/3d/vegetable_manager.h" #include "nel/3d/driver.h" #include "nel/3d/texture_file.h" #include "nel/misc/fast_floor.h" #include "nel/3d/vegetable_quadrant.h" #include "nel/3d/dru.h" #include "nel/3d/radix_sort.h" #include "nel/3d/scene.h" #include "nel/3d/vegetable_blend_layer_model.h" #include "nel/3d/vegetable_light_ex.h" #include "nel/misc/hierarchical_timer.h" #include using namespace std; using namespace NLMISC; namespace NL3D { #define NL3D_VEGETABLE_CLIP_BLOCK_BLOCKSIZE 16 #define NL3D_VEGETABLE_SORT_BLOCK_BLOCKSIZE 64 #define NL3D_VEGETABLE_INSTANCE_GROUP_BLOCKSIZE 128 // *************************************************************************** CVegetableManager::CVegetableManager(uint maxVertexVbHardUnlit, uint maxVertexVbHardLighted, uint nbBlendLayers, float blendLayerDistMax) : _ClipBlockMemory(NL3D_VEGETABLE_CLIP_BLOCK_BLOCKSIZE), _SortBlockMemory(NL3D_VEGETABLE_SORT_BLOCK_BLOCKSIZE), _InstanceGroupMemory(NL3D_VEGETABLE_INSTANCE_GROUP_BLOCKSIZE), _GlobalDensity(1.f), _NumZSortBlendLayers(nbBlendLayers), _ZSortLayerDistMax(blendLayerDistMax), _ZSortScene(NULL) { uint i; // Init all the allocators nlassert((uint)(CVegetableVBAllocator::VBTypeCount) == 2); _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted].init( CVegetableVBAllocator::VBTypeLighted, maxVertexVbHardLighted ); _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit].init( CVegetableVBAllocator::VBTypeUnlit, maxVertexVbHardUnlit ); // Init soft one, with no vbHard vertices. _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted].init( CVegetableVBAllocator::VBTypeLighted, 0 ); _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit].init( CVegetableVBAllocator::VBTypeUnlit, 0 ); // NB Vertex programs are initilized during the first call to update driver. // setup the material. Unlit (doesn't matter, lighting in VP) Alpha Test. _VegetableMaterial.initUnlit(); _VegetableMaterial.setAlphaTest(true); _VegetableMaterial.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha); // default light. _DirectionalLight= (CVector(0,1, -1)).normed(); _GlobalAmbient.set(64, 64, 64, 255); _GlobalDiffuse.set(150, 150, 150, 255); // Wind. _WindDirection.set(1,0,0); _WindFrequency= 1; _WindPower= 1; _WindBendMin= 0; _Time= 0; _WindPrecRenderTime= 0; _WindAnimTime= 0; // Init CosTable. for(i=0; ideleteModel(_ZSortModelLayers[i]); _ZSortModelLayers[i]= NULL; _ZSortScene->deleteModel(_ZSortModelLayersUW[i]); _ZSortModelLayersUW[i]= NULL; } _ZSortScene= NULL; } } // *************************************************************************** void CVegetableManager::createVegetableBlendLayersModels(CScene *scene) { // setup scene nlassert(scene); _ZSortScene= scene; // create the layers models. for(uint i=0;i<_NumZSortBlendLayers; i++) { // assert not already done. nlassert(_ZSortModelLayers[i]==NULL); nlassert(_ZSortModelLayersUW[i]==NULL); _ZSortModelLayers[i]= (CVegetableBlendLayerModel*)scene->createModel(VegetableBlendLayerModelId); _ZSortModelLayersUW[i]= (CVegetableBlendLayerModel*)scene->createModel(VegetableBlendLayerModelId); // init owner. _ZSortModelLayers[i]->VegetableManager= this; _ZSortModelLayersUW[i]->VegetableManager= this; // Set UnderWater layer for _ZSortModelLayersUW _ZSortModelLayersUW[i]->setOrderingLayer(2); } } // *************************************************************************** CVegetableVBAllocator &CVegetableManager::getVBAllocatorForRdrPassAndVBHardMode(uint rdrPass, uint vbHardMode) { // If software VB if(vbHardMode==0) { if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED) return _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED) return _VBSoftAllocator[CVegetableVBAllocator::VBTypeLighted]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT) return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED) return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) return _VBSoftAllocator[CVegetableVBAllocator::VBTypeUnlit]; } // If hard VB else { if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED) return _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED) return _VBHardAllocator[CVegetableVBAllocator::VBTypeLighted]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT) return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED) return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit]; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) return _VBHardAllocator[CVegetableVBAllocator::VBTypeUnlit]; } // abnormal case nlstop; // To avoid warning; return _VBSoftAllocator[0]; } // *************************************************************************** // *************************************************************************** // Vertex Program. // *************************************************************************** // *************************************************************************** // *************************************************************************** /* Vegetable, without bend for now. Inputs -------- v[0] == Pos to Center of the vegetable in world space. v[10] == Center of the vegetable in world space. v[2] == Normal (present if lighted only) v[3] == Color (if unlit) or DiffuseColor (if lighted) v[4] == SecondaryColor (==ambient if Lighted, and use only Alpha part for DLM if Unlit) v[8] == Tex0 (xy) v[9] == BendInfo (xyz) = {BendWeight/2, BendPhase, BendFrequencyFactor} NB: /2 because compute a quaternion Changes: If unlit, then small changes: v[0] == Pos to center, with v[0].w == BendWeight * v[0].norm() v[9] == BendInfo/BlendInfo (xyzw) = {v[0].norm(), BendPhase, BendFrequencyFactor, BlendDist} NB: v[9].w. is used only in Unlit+2Sided+AlphaBlend. But prefer do this for gestion purpose: to have only one VBAllocator for all modes. NB: Color and Secondary color Alpha Part contains Dynamic LightMap UV, (in 8 bits). Constant: -------- Setuped at beginning of CVegetableManager::render() c[0..3]= ModelViewProjection Matrix. c[6]= Fog vector. c[8]= {0, 1, 0.5, 2} c[9]= unit world space Directionnal light. c[10]= camera pos in world space. c[11]= {1/DistBlendTransition} NB: DiffuseColor and AmbientColor of vertex must have been pre-multiplied by lightColor // Bend: c[16]= quaternion axis. w==1, and z must be 0 c[17]= { timeAnim , WindPower, WindPower*(1-WindBendMin)/2, 0 } c[18]= High order Taylor cos coefficient: { -1/2, 1/24, -1/720, 1/40320 } c[19]= Low order Taylor cos coefficient: { 1, -1/2, 1/24, -1/720 } c[20]= Low order Taylor sin coefficient: { 1, -1/6, 1/120, -1/5040 } c[21]= Special constant vector for quatToMatrix: { 0, 1, -1, 0 } c[22]= {0.5, Pi, 2*Pi, 1/(2*Pi)} c[23]= {64, 0, 0, 0} (size of the LUT) // Bend Lut: c[32..95] 64 Lut entries for cos-like animation Fog Note: ----------- Fog should be disabled, because not computed (for speed consideration and becasue micro-vegetation should never be in Fog). Speed Note: ----------- Max program length (lighted/2Sided) is: 29 (bend-quaternion) + 16 (rotNormal + bend + lit 2Sided) + 5 (proj + tex) 2 (Dynamic lightmap copy) 51 Normal program length (unlit/2Sided/No Alpha Blend) is: 12 (bend-delta) + 1 (unlit) + 5 (proj + tex) 2 (Dynamic lightmap copy) 20 AlphaBlend program length (unlit/2Sided/Alpha Blend) is: 12 (bend-delta) + 1 (unlit) + 5 (Alpha Blend) 5 (proj + tex) 2 (Dynamic lightmap copy) 26 */ // *********************** /* Fast (but less accurate) Bend program: Result: bend pos into R5, */ // *********************** const char* NL3D_FastBendProgram= "!!VP1.0 \n\ # compute time of animation: time*freqfactor + phase. \n\ MAD R0.x, c[17].x, v[9].z, v[9].y; # R0.x= time of animation \n\ \n\ # animation: use the 64 LUT entries \n\ EXP R0.y, R0.x; # fract part=> R0.y= [0,1[ \n\ MUL R0, R0.y, c[23].xyyy; # R0.x= [0,64[ \n\ ARL A0.x, R0.x; # A0.x= index in the LUT \n\ EXP R0.y, R0.x; # R0.y= R0.x-A0.x= fp (fract part) \n\ # lookup and lerp in one it: R0= value + fp * dv. \n\ MAD R0.xy, R0.y, c[A0.x+32].zwww, c[A0.x+32].xyww; \n\ \n\ # The direction and power of the wind is encoded in the LUT. \n\ # Scale now by vertex BendFactor (stored in v[0].w) \n\ MAD R5, R0, v[0].w, v[0].xyzw; \n\ # compute 1/norm, and multiply by original norm stored in v[9].x \n\ DP3 R0.x, R5, R5; \n\ RSQ R0.x, R0.x; \n\ MUL R0.x, R0.x, v[9].x; \n\ # mul by this factor, and add to center \n\ MAD R5, R0.xxxw, R5, v[10]; \n\ \n\ # make local to camera pos. Important for ZBuffer precision \n\ ADD R5, R5, -c[10]; \n\ "; // Test /*const char* NL3D_FastBendProgram= "!!VP1.0 \n\ # compute time of animation: time + phase. \n\ ADD R0.x, c[17].x, v[9].y; # R0.x= time of animation \n\ \n\ # animation: f(x)= cos(x). compute a high precision cosinus \n\ EXP R0.y, R0.x; # fract part=> R0.y= [0,1] <=> [-Pi, Pi] \n\ MAD R0.x, R0.y, c[22].z, -c[22].y; # R0.x= a= [-Pi, Pi] \n\ # R0 must get a2, a4, a6, a8 \n\ MUL R0.x, R0.x, R0.x; # R0.x= a2 \n\ MUL R0.y, R0.x, R0.x; # R0= a2, a4 \n\ MUL R0.zw, R0.y, R0.xxxy; # R0= a2, a4, a6, a8 \n\ # Taylor serie: cos(x)= 1 - (1/2) a2 + (1/24) a4 - (1/720) a6 + (1/40320) a8. \n\ DP4 R0.x, R0, c[18]; # R0.x= cos(x) - 1. \n\ \n\ \n\ # original norm \n\ DP3 R2.x, v[0], v[0]; \n\ RSQ R2.y, R2.x; \n\ MUL R2.x, R2.x, R2.y; \n\ # norm, mul by factor, and add to relpos \n\ ADD R1.x, R0.x, c[8].w; \n\ MUL R0.x, v[9].x, R2.x; \n\ MUL R1, R1, R0.x; \n\ ADD R5.xyz, R1, v[0]; \n\ # mod norm \n\ DP3 R0.x, R5, R5; \n\ RSQ R0.x, R0.x; \n\ MUL R0.x, R0.x, R2.x; \n\ MAD R5, R0.x, R5, v[10]; \n\ ";*/ // *********************** /* Bend start program: Result: bend pos into R5, and R7,R8,R9 is the rotation matrix for possible normal lighting. */ // *********************** // Splitted in 2 parts because of the 2048 char limit const char* NL3D_BendProgramP0= "!!VP1.0 \n\ # compute time of animation: time*freqfactor + phase. \n\ MAD R0.x, c[17].x, v[9].z, v[9].y; # R0.x= time of animation \n\ \n\ # animation: f(x)= cos(x). compute a high precision cosinus \n\ EXP R0.y, R0.x; # fract part=> R0.y= [0,1] <=> [-Pi, Pi] \n\ MAD R0.x, R0.y, c[22].z, -c[22].y; # R0.x= a= [-Pi, Pi] \n\ # R0 must get a2, a4, a6, a8 \n\ MUL R0.x, R0.x, R0.x; # R0.x= a2 \n\ MUL R0.y, R0.x, R0.x; # R0= a2, a4 \n\ MUL R0.zw, R0.y, R0.xxxy; # R0= a2, a4, a6, a8 \n\ # Taylor serie: cos(x)= 1 - (1/2) a2 + (1/24) a4 - (1/720) a6 + (1/40320) a8. \n\ DP4 R0.x, R0, c[18]; # R0.x= cos(x) - 1. \n\ \n\ # R0.x= [-2, 0]. And we want a result in BendWeight/2*WindPower*[WindBendMin, 1] \n\ MAD R0.x, R0.x, c[17].z, c[17].y; # R0.x= WindPower*[WindBendMin, 1] \n\ MUL R0.x, R0.x, v[9].x; # R0.x= angle= BendWeight/2*WindPower*[WindBendMin, 1] \n\ \n\ # compute good precision sinus and cosinus, in R0.xy. \n\ # suppose that BendWeightMax/2== 2Pi/3 => do not need to fmod() nor \n\ # to have high order taylor serie \n\ DST R1.xy, R0.x, R0.x; # R1= 1, a2 \n\ MUL R1.z, R1.y, R1.y; # R1= 1, a2, a4 \n\ MUL R1.w, R1.y, R1.z; # R1= 1, a2, a4, a6 (cos serie) \n\ MUL R2, R1, R0.x; # R2= a, a3, a5, a7 (sin serie) \n\ DP4 R0.x, R1, c[19]; # R0.x= cos(a) \n\ DP4 R0.y, R2, c[20]; # R0.y= sin(a) \n\ "; const char* NL3D_BendProgramP1= " \n\ # build our quaternion \n\ # multiply the angleAxis by sin(a) / cos(a), where a is actually a/2 \n\ # remind: c[16].z== angleAxis.z== 0 \n\ MUL R0, c[16], R0.yyyx; # R0= quaternion.xyzw \n\ \n\ # build our matrix from this quaternion, into R7,R8,R9 \n\ # Quaternion TO matrix 3x3 in 7 ope, with quat.z==0 \n\ MUL R1, R0, c[8].w; # R1= quat2= 2*quat == 2*x, 2*y, 0, 2*w \n\ MUL R2, R1, R0.x; # R2= quatX= xx, xy, 0, wx \n\ MUL R3, R1, R0.y; # R3= quatY= xy, yy, 0, wy \n\ # NB: c[21]= {0, 1, -1, 0} \n\ # Write to w, then w = 0, this avoid an unitialized component \n\ MAD R7.xyzw, c[21].zyyw, R3.yxww, c[21].yxxw; \n\ # R7.x= a11 = 1.0f - (yy) \n\ # R7.y= a12 = xy \n\ # R7.z= a13 = wy \n\ # NB: c[21]= {0, 1, -1, 0} \n\ # Write to w, then w = 0, this avoid an unitialized component \n\ MAD R8.xyzw, c[21].yzzw, R2.yxww, c[21].xyxw; \n\ # R8.x= a21 = xy \n\ # R8.y= a22 = 1.0f - (xx) \n\ # R8.z= a23 = - wx \n\ # NB: c[21]= {0, 1, -1, 0} \n\ # Write to w, then w = 0, this avoid an unitialized component \n\ ADD R9.xyzw, R2.zwxw, R3.wzyw; # a31= 0+wy, a32= wx+0, a33= xx + yy, because z==0 \n\ MAD R9.xyzw, R9.xyzw, c[21].zyzw, c[21].xxyw; \n\ # R9.x= a31 = - wy \n\ # R9.y= a32 = wx \n\ # R9.z= a33 = 1.0f - (xx + yy) \n\ # transform pos \n\ DP3 R5.x, R7, v[0]; \n\ DP3 R5.y, R8, v[0]; \n\ DP3 R5.z, R9, v[0]; # R5= bended relative pos to center. \n\ #temp, to optimize \n\ MOV R5.w, c[21].w; \n\ # add pos to center pos. \n\ ADD R5, R5, v[10]; # R5= world pos. R5.w= R5.w+v[10].w= 0+1= 1 \n\ # make local to camera pos. Important for ZBuffer precision \n\ ADD R5, R5, -c[10]; \n\ "; // Concat the 2 strings const string NL3D_BendProgram= string(NL3D_BendProgramP0) + string(NL3D_BendProgramP1); // *********************** /* Lighted start program: bend pos and normal, normalize and lit */ // *********************** // Common start program. const char* NL3D_LightedStartVegetableProgram= " \n\ # bend Pos into R5. Now do it for normal \n\ DP3 R0.x, R7, v[2]; \n\ DP3 R0.y, R8, v[2]; \n\ DP3 R0.z, R9, v[2]; # R0= matRot * normal. \n\ # Do the rot 2 times for normal (works fine) \n\ DP3 R6.x, R7, R0; \n\ DP3 R6.y, R8, R0; \n\ DP3 R6.z, R9, R0; # R6= bended normal. \n\ \n\ # Normalize normal, and dot product, into R0.x \n\ # w hasn't been written \n\ DP3 R0.x, R6.xyzz, R6.xyzz; # R0.x= R6.sqrnorm() \n\ RSQ R0.x, R0.x; # R0.x= 1/norm() \n\ MUL R6, R6.xyzz, R0.x; # R6= R6.normed() \n\ DP3 R0.x, R6, c[9]; \n\ \n\ #FrontFacing \n\ MAX R0.y, -R0.x, c[8].x; # R0.y= diffFactor= max(0, -R6*LightDir) \n\ MUL R1.xyz, R0.y, v[3]; # R7= diffFactor*DiffuseColor \n\ ADD o[COL0].xyz, R1, v[4]; # col0.RGB= AmbientColor + diffFactor*DiffuseColor \n\ MOV o[COL0].w, c[8].y; \n\ "; // *********************** /* Unlit start program: bend pos into R5, and copy color(s) */ // *********************** // Unlit no alpha blend. const char* NL3D_UnlitVegetableProgram= " MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\ \n\ MOV o[COL0].w, c[8].y; \n\ "; // Unlit with AlphaBlend. const char* NL3D_UnlitAlphaBlendVegetableProgram= " MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\ \n\ #Blend transition. NB: in R5, we already have the position relative to the camera \n\ DP3 R0.x, R5, R5; # R0.x= sqr(dist to viewer). \n\ RSQ R0.y, R0.x; \n\ MUL R0.x, R0.x, R0.y; # R0.x= dist to viewer \n\ # setup alpha Blending. Distance of appartition is encoded in the vertex. \n\ MAD o[COL0].w, R0.x, c[11].x, v[9].w; \n\ "; // *********************** /* Common end of program: project, texture. Take pos from R5 */ // *********************** const char* NL3D_CommonEndVegetableProgram= " # compute in Projection space \n\ DP4 o[HPOS].x, c[0], R5; \n\ DP4 o[HPOS].y, c[1], R5; \n\ DP4 o[HPOS].z, c[2], R5; \n\ DP4 o[HPOS].w, c[3], R5; \n\ # copy Dynamic lightmap UV in stage0, from colors Alpha part. \n\ MAD o[TEX0].xzw, v[3].w, c[8].yxxx, c[8].xxxy; \n\ MOV o[TEX0].y, v[4].w; \n\ # copy diffuse texture uv to stage 1. \n\ MOV o[TEX1], v[8]; \n\ "; // fogged version const char* NL3D_VegetableProgramFog = " DP4 o[FOGC].x, c[6], R5; \n\ "; // *********************** /* Speed test VP, No bend,no lighting. */ // *********************** const char* NL3D_SimpleStartVegetableProgram= "!!VP1.0 \n\ # compute in Projection space \n\ MAD R5, v[0], c[8].yyyx, c[8].xxxy; \n\ ADD R5.xyz, R5, v[10]; \n\ # make local to camera pos \n\ ADD R5, R5, -c[10]; \n\ MOV o[COL0].xyz, v[3]; # col.RGBA= vertex color \n\ "; // *************************************************************************** void CVegetableManager::initVertexProgram(uint vpType, bool fogEnabled) { nlassert(_LastDriver); // update driver should have been called at least once ! // Init the Vertex Program. string vpgram; // start always with Bend. if( vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED || vpType==NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED ) vpgram= NL3D_BendProgram; else vpgram= NL3D_FastBendProgram; // combine the VP according to Type switch(vpType) { case NL3D_VEGETABLE_RDRPASS_LIGHTED: case NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED: vpgram+= string(NL3D_LightedStartVegetableProgram); break; case NL3D_VEGETABLE_RDRPASS_UNLIT: case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED: vpgram+= string(NL3D_UnlitVegetableProgram); break; case NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT: vpgram+= string(NL3D_UnlitAlphaBlendVegetableProgram); break; } // common end of VP vpgram+= string(NL3D_CommonEndVegetableProgram); if (fogEnabled) { vpgram+= string(NL3D_VegetableProgramFog); } vpgram+="\nEND\n"; // create VP. _VertexProgram[vpType][fogEnabled ? 1 : 0] = new CVertexProgram(vpgram.c_str()); } // *************************************************************************** // *************************************************************************** // Instanciation // *************************************************************************** // *************************************************************************** // *************************************************************************** CVegetableClipBlock *CVegetableManager::createClipBlock() { // create a clipblock CVegetableClipBlock *ret; ret= _ClipBlockMemory.allocate(); // append to list. _EmptyClipBlockList.append(ret); return ret; } // *************************************************************************** void CVegetableManager::deleteClipBlock(CVegetableClipBlock *clipBlock) { if(!clipBlock) return; // verify no more sortBlocks in this clipblock nlassert(clipBlock->_SortBlockList.size() == 0); // unlink from _EmptyClipBlockList, because _InstanceGroupList.size() == 0 ... _EmptyClipBlockList.remove(clipBlock); // delete _ClipBlockMemory.free(clipBlock); } // *************************************************************************** CVegetableSortBlock *CVegetableManager::createSortBlock(CVegetableClipBlock *clipBlock, const CVector ¢er, float radius) { nlassert(clipBlock); // create a clipblock CVegetableSortBlock *ret; ret= _SortBlockMemory.allocate(); ret->_Owner= clipBlock; ret->_Center= center; ret->_Radius= radius; // append to list. clipBlock->_SortBlockList.append(ret); return ret; } // *************************************************************************** void CVegetableManager::deleteSortBlock(CVegetableSortBlock *sortBlock) { if(!sortBlock) return; // verify no more IGs in this sortblock nlassert(sortBlock->_InstanceGroupList.size() == 0); // unlink from clipBlock sortBlock->_Owner->_SortBlockList.remove(sortBlock); // delete _SortBlockMemory.free(sortBlock); } // *************************************************************************** CVegetableInstanceGroup *CVegetableManager::createIg(CVegetableSortBlock *sortBlock) { nlassert(sortBlock); CVegetableClipBlock *clipBlock= sortBlock->_Owner; // create an IG CVegetableInstanceGroup *ret; ret= _InstanceGroupMemory.allocate(); ret->_SortOwner= sortBlock; ret->_ClipOwner= clipBlock; // if the clipBlock is empty, change list, because won't be no more. if(clipBlock->_NumIgs==0) { // remove from empty list _EmptyClipBlockList.remove(clipBlock); // and append to not empty one. _ClipBlockList.append(clipBlock); } // inc the number of igs appended to the clipBlock. clipBlock->_NumIgs++; // link ig to sortBlock. sortBlock->_InstanceGroupList.append(ret); // Special Init: The ZSort rdrPass must start with the same HardMode than SortBlock. ret->_RdrPass[NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT].HardMode= sortBlock->ZSortHardMode; return ret; } // *************************************************************************** void CVegetableManager::deleteIg(CVegetableInstanceGroup *ig) { if(!ig) return; // update lighting mgt: no more vertices. // ----------- // If I delete the ig which is the current root if(_ULRootIg == ig) { // switch to next _ULRootIg= ig->_ULNext; // if still the same, it means that the circular list is now empty if(_ULRootIg == ig) _ULRootIg= NULL; // Reset UL instance info. _ULCurrentIgRdrPass= 0; _ULCurrentIgInstance= 0; } // remove UL vertex count of the deleted ig _ULNTotalVertices-= ig->_ULNumVertices; // unlink the ig for lighting update. ig->unlinkUL(); // For all render pass of this instance, delete his vertices // ----------- for(sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++) { // rdrPass CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass]; // which allocator? CVegetableVBAllocator &vbAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vegetRdrPass.HardMode); // For all vertices of this rdrPass, delete it sint numVertices; numVertices= vegetRdrPass.Vertices.size(); // all vertices must have been setuped. nlassert((uint)numVertices == vegetRdrPass.NVertices); for(sint i=0; i_ClipOwner; CVegetableSortBlock *sortBlock= ig->_SortOwner; // If I have got some faces in ZSort rdrPass if(ig->_HasZSortPassInstances) // after my deletion, the sortBlock must be updated. sortBlock->_Dirty= true; // unlink from sortBlock, and delete. sortBlock->_InstanceGroupList.remove(ig); _InstanceGroupMemory.free(ig); // decRef the clipBlock clipBlock->_NumIgs--; // if the clipBlock is now empty, change list if(clipBlock->_NumIgs==0) { // remove from normal list _ClipBlockList.remove(clipBlock); // and append to empty list. _EmptyClipBlockList.append(clipBlock); } } // *************************************************************************** CVegetableShape *CVegetableManager::getVegetableShape(const std::string &shape) { ItShapeMap it= _ShapeMap.find(shape); // if found if(it != _ShapeMap.end()) return &it->second; // else insert { // insert. CVegetableShape *ret; it= ( _ShapeMap.insert(make_pair(shape, CVegetableShape()) ) ).first; ret= &it->second; // fill. try { if( !ret->loadShape(shape) ) { // Warning nlwarning ("CVegetableManager::getVegetableShape could not load shape file '%s'", shape.c_str ()); // Remove from map _ShapeMap.erase (shape); // Return NULL ret = NULL; } } catch (Exception &e) { // Warning nlwarning ("CVegetableManager::getVegetableShape error while loading shape file '%s' : '%s'", shape.c_str (), e.what ()); // Remove from map _ShapeMap.erase (shape); // Return NULL ret = NULL; } return ret; } } // *************************************************************************** uint CVegetableManager::getRdrPassInfoForShape(CVegetableShape *shape, TVegetableWater vegetWaterState, bool &instanceLighted, bool &instanceDoubleSided, bool &instanceZSort, bool &destLighted, bool &precomputeLighting) { instanceLighted= shape->Lighted; instanceDoubleSided= shape->DoubleSided; // Disable ZSorting when we intersect water. instanceZSort= shape->AlphaBlend && vegetWaterState!=IntersectWater; destLighted= instanceLighted && !shape->PreComputeLighting; precomputeLighting= instanceLighted && shape->PreComputeLighting; // get correct rdrPass uint rdrPass; // get according to lighted / doubleSided state if(destLighted) { if(instanceDoubleSided) rdrPass= NL3D_VEGETABLE_RDRPASS_LIGHTED_2SIDED; else rdrPass= NL3D_VEGETABLE_RDRPASS_LIGHTED; } else { if(instanceDoubleSided) { if(instanceZSort) rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT; else rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED; } else rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT; } return rdrPass; } // *************************************************************************** void CVegetableManager::reserveIgAddInstances(CVegetableInstanceGroupReserve &vegetIgReserve, CVegetableShape *shape, TVegetableWater vegetWaterState, uint numInstances) { bool instanceLighted; bool instanceDoubleSided; bool instanceZSort; bool destLighted; bool precomputeLighting; // get correct rdrPass / info uint rdrPass; rdrPass= getRdrPassInfoForShape(shape, vegetWaterState, instanceLighted, instanceDoubleSided, instanceZSort, destLighted, precomputeLighting); // veget rdrPass CVegetableInstanceGroupReserve::CVegetableRdrPass &vegetRdrPass= vegetIgReserve._RdrPass[rdrPass]; // Reserve space in the rdrPass. vegetRdrPass.NVertices+= numInstances * shape->VB.getNumVertices(); vegetRdrPass.NTriangles+= numInstances * shape->TriangleIndices.size()/3; // if the instances are lighted, reserve space for lighting updates if(instanceLighted) vegetRdrPass.NLightedInstances+= numInstances; } // *************************************************************************** void CVegetableManager::reserveIgCompile(CVegetableInstanceGroup *ig, const CVegetableInstanceGroupReserve &vegetIgReserve) { uint rdrPass; // Check. //=========== // For all rdrPass of the ig, check empty for(rdrPass= 0; rdrPass_RdrPass[rdrPass]; nlassert(vegetRdrPass.TriangleIndices.getNumIndexes()==0); nlassert(vegetRdrPass.TriangleLocalIndices.size()==0); nlassert(vegetRdrPass.Vertices.size()==0); nlassert(vegetRdrPass.LightedInstances.size()==0); } // Do the same for all quadrants of the zsort rdrPass. nlassert(ig->_TriangleQuadrantOrderArray.size()==0); nlassert(ig->_TriangleQuadrantOrderNumTriangles==0); // Reserve. //=========== // For all rdrPass of the ig, reserve. for(rdrPass= 0; rdrPass_RdrPass[rdrPass]; uint numVertices= vegetIgReserve._RdrPass[rdrPass].NVertices; uint numTris= vegetIgReserve._RdrPass[rdrPass].NTriangles; uint numLightedInstances= vegetIgReserve._RdrPass[rdrPass].NLightedInstances; // reserve triangles indices and vertices for this rdrPass. vegetRdrPass.TriangleIndices.setFormat(vegetRdrPass.HardMode ? CIndexBuffer::Indices16 : CIndexBuffer::Indices32); vegetRdrPass.TriangleIndices.setNumIndexes(numTris*3); vegetRdrPass.TriangleLocalIndices.resize(numTris*3); vegetRdrPass.Vertices.resize(numVertices); // reserve ligthedinstances space. vegetRdrPass.LightedInstances.resize(numLightedInstances); } // Reserve space for the zsort rdrPass sorting. uint numZSortTris= vegetIgReserve._RdrPass[NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT].NTriangles; // allocate sufficient space for all quadrants (1 alloc for all quadrants). ig->_TriangleQuadrantOrderArray.resize(numZSortTris * NL3D_VEGETABLE_NUM_QUADRANT); // And init ptrs. if(numZSortTris>0) { sint16 *start= ig->_TriangleQuadrantOrderArray.getPtr(); // init ptr to each qaudrant for(uint i=0; i_TriangleQuadrantOrders[i]= start + i*numZSortTris; } } } // *************************************************************************** inline void computeVegetVertexLighting(const CVector &rotNormal, const CVector &sunDir, CRGBA primaryRGBA, CRGBA secondaryRGBA, CVegetableLightEx &vegetLex, CRGBA diffusePL[2], CRGBA *dst) { float dpSun; float dpPL[2]; CRGBA col; CRGBA resColor; // compute front-facing coloring. { // Compute Sun Light. dpSun= rotNormal*sunDir; float f= max(0.f, -dpSun); col.modulateFromuiRGBOnly(primaryRGBA, NLMISC::OptFastFloor(f*256)); // Add it with ambient resColor.addRGBOnly(col, secondaryRGBA); // Add influence of 2 lights only. (unrolled for better BTB use) // Compute Light 0 ? if(vegetLex.NumLights>=1) { dpPL[0]= rotNormal*vegetLex.Direction[0]; f= max(0.f, -dpPL[0]); col.modulateFromuiRGBOnly(diffusePL[0], NLMISC::OptFastFloor(f*256)); resColor.addRGBOnly(col, resColor); // Compute Light 1 ? if(vegetLex.NumLights>=2) { dpPL[1]= rotNormal*vegetLex.Direction[1]; f= max(0.f, -dpPL[1]); col.modulateFromuiRGBOnly(diffusePL[1], NLMISC::OptFastFloor(f*256)); resColor.addRGBOnly(col, resColor); } } // Keep correct U of Dynamic Lightmap UV encoded in primaryRGBA Alpha part. resColor.A= primaryRGBA.A; // copy to dest *dst= resColor; } } // *************************************************************************** inline void computeVegetVertexLightingForceBestSided(const CVector &rotNormal, const CVector &sunDir, CRGBA primaryRGBA, CRGBA secondaryRGBA, CVegetableLightEx &vegetLex, CRGBA diffusePL[2], CRGBA *dst) { float dpSun; float dpPL[2]; CRGBA col; CRGBA resColor; // compute best-facing coloring. { // Compute Sun Light. dpSun= rotNormal*sunDir; // ForceBestSided: take the absolute value (max of -val,val) float f= (float)fabs(dpSun); col.modulateFromuiRGBOnly(primaryRGBA, NLMISC::OptFastFloor(f*256)); // Add it with ambient resColor.addRGBOnly(col, secondaryRGBA); // Add influence of 2 lights only. (unrolled for better BTB use) // Compute Light 0 ? if(vegetLex.NumLights>=1) { dpPL[0]= rotNormal*vegetLex.Direction[0]; // ForceBestSided: take the absolute value (max of -val,val) f= (float)fabs(dpPL[0]); col.modulateFromuiRGBOnly(diffusePL[0], NLMISC::OptFastFloor(f*256)); resColor.addRGBOnly(col, resColor); // Compute Light 1 ? if(vegetLex.NumLights>=2) { dpPL[1]= rotNormal*vegetLex.Direction[1]; f= (float)fabs(dpPL[1]); col.modulateFromuiRGBOnly(diffusePL[1], NLMISC::OptFastFloor(f*256)); resColor.addRGBOnly(col, resColor); } } // Keep correct U of Dynamic Lightmap UV encoded in primaryRGBA Alpha part. resColor.A= primaryRGBA.A; // copy to dest *dst= resColor; } } // *************************************************************************** void CVegetableManager::addInstance(CVegetableInstanceGroup *ig, CVegetableShape *shape, const NLMISC::CMatrix &mat, const NLMISC::CRGBAF &ambientColor, const NLMISC::CRGBAF &diffuseColor, float bendFactor, float bendPhase, float bendFreqFactor, float blendDistMax, TVegetableWater vegetWaterState, CVegetableUV8 dlmUV) { sint i; // Some setup. //-------------------- bool instanceLighted; bool instanceDoubleSided; bool instanceZSort; bool destLighted; bool precomputeLighting; // get correct rdrPass / info uint rdrPass; rdrPass= getRdrPassInfoForShape(shape, vegetWaterState, instanceLighted, instanceDoubleSided, instanceZSort, destLighted, precomputeLighting); // bestSided Precompute lighting or not?? bool bestSidedPrecomputeLighting= precomputeLighting && shape->BestSidedPreComputeLighting; // veget rdrPass CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass]; // color. // setup using OptFastFloor. CRGBA ambientRGBA, diffuseRGBA; CRGBA primaryRGBA, secondaryRGBA; // diffuseColor diffuseRGBA.R= (uint8)NLMISC::OptFastFloor(diffuseColor.R*255); diffuseRGBA.G= (uint8)NLMISC::OptFastFloor(diffuseColor.G*255); diffuseRGBA.B= (uint8)NLMISC::OptFastFloor(diffuseColor.B*255); diffuseRGBA.A= 255; // ambientColor ambientRGBA.R= (uint8)NLMISC::OptFastFloor(ambientColor.R*255); ambientRGBA.G= (uint8)NLMISC::OptFastFloor(ambientColor.G*255); ambientRGBA.B= (uint8)NLMISC::OptFastFloor(ambientColor.B*255); ambientRGBA.A= 255; // For Lighted, modulate with global light. if(instanceLighted) { primaryRGBA.modulateFromColorRGBOnly(diffuseRGBA, _GlobalDiffuse); secondaryRGBA.modulateFromColorRGBOnly(ambientRGBA, _GlobalAmbient); } // if the instance is not lighted, then don't take care of lighting else { primaryRGBA.R= diffuseRGBA.R; primaryRGBA.G= diffuseRGBA.G; primaryRGBA.B= diffuseRGBA.B; // may not be useful (2Sided lighting no more supported) secondaryRGBA= primaryRGBA; } // Copy Dynamic Lightmap UV in Alpha part (save memory for an extra cost of 1 VP instruction) primaryRGBA.A= dlmUV.U; secondaryRGBA.A= dlmUV.V; // get ref on the vegetLex. CVegetableLightEx &vegetLex= ig->VegetableLightEx; // Color of pointLights modulated by diffuse. CRGBA diffusePL[2]; if(vegetLex.NumLights>=1) { diffusePL[0].modulateFromColorRGBOnly(diffuseRGBA, vegetLex.Color[0]); if(vegetLex.NumLights>=2) { diffusePL[1].modulateFromColorRGBOnly(diffuseRGBA, vegetLex.Color[1]); } } // normalize bendFreqFactor bendFreqFactor*= NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC; bendFreqFactor= (float)floor(bendFreqFactor + 0.5f); bendFreqFactor/= NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC; // Get allocator, and manage VBhard overriding. //-------------------- CVegetableVBAllocator *allocator; // if still in Sfot mode, keep it. if(!vegetRdrPass.HardMode) { // get the soft allocator. allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 0); } else { // Get VB allocator Hard for this rdrPass allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 1); // Test if the instance don't add too many vertices for this VBHard if(allocator->exceedMaxVertexInBufferHard(shape->VB.getNumVertices())) { // if exceed, then must pass ALL the IG in software mode. vertices/faces are correclty updated. // special: if rdrPass is the ZSort one, if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) { nlassert(ig->_SortOwner->ZSortHardMode); // must do it on ALL igs of the sortBlock, for less VBuffer mode switching. CVegetableInstanceGroup *pIg= ig->_SortOwner->_InstanceGroupList.begin(); while(pIg) { // let's pass them in software mode. swapIgRdrPassHardMode(pIg, rdrPass); // next pIg= (CVegetableInstanceGroup*)pIg->Next; } // Then all The sortBlock is in SoftMode. ig->_SortOwner->ZSortHardMode= false; } else { // just do it on this Ig (can mix hardMode in a SortBlock for normal rdrPass) swapIgRdrPassHardMode(ig, rdrPass); } // now, we can use the software only Allocator to append our instance allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPass, 0); } } // get correct dstVB const CVertexBuffer &dstVBInfo= allocator->getSoftwareVertexBuffer(); // Transform vertices to a vegetable instance, and enlarge clipBlock //-------------------- // compute matrix to multiply normals, ie (M-1)t CMatrix normalMat; // need just rotation scale matrix. normalMat.setRot(mat); normalMat.invert(); normalMat.transpose(); // compute Instance position CVector instancePos; mat.getPos(instancePos); // At least, the bbox of the clipBlock must include the center of the shape. ig->_ClipOwner->extendSphere(instancePos); // Vertex/triangle Info. uint numNewVertices= shape->VB.getNumVertices(); uint numNewTris= shape->TriangleIndices.size()/3; uint numNewIndices= shape->TriangleIndices.size(); // src info. uint srcNormalOff= (instanceLighted? shape->VB.getNormalOff() : 0); uint srcTex0Off= shape->VB.getTexCoordOff(0); uint srcTex1Off= shape->VB.getTexCoordOff(1); // dst info uint dstNormalOff= (destLighted? dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_NORMAL) : 0); uint dstColor0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR0); uint dstColor1Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR1); uint dstTex0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_TEX0); uint dstBendOff= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_BENDINFO); uint dstCenterOff= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_CENTER); // For D3D, If the VertexBuffer is in BGRA mode if(allocator->isBGRA()) { // then swap only the B and R (no cpu cycle added per vertex) primaryRGBA.swapBR(); secondaryRGBA.swapBR(); diffusePL[0].swapBR(); diffusePL[1].swapBR(); } // Useful for !destLighted only. CVector deltaPos; float deltaPosNorm=0.0; // Useful for ZSORT rdrPass, the worldVertices. static vector worldVertices; if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) { worldVertices.resize(numNewVertices); } CVertexBufferRead vba; shape->VB.lock (vba); // For all vertices of shape, transform and store manager indices in temp shape. for(i=0; i<(sint)numNewVertices;i++) { // allocate a Vertex uint vid= allocator->allocateVertex(); CVertexBufferReadWrite vbaOut; allocator->getSoftwareVertexBuffer ().lock(vbaOut); // store in tmp shape. shape->InstanceVertices[i]= vid; // Fill this vertex. const uint8 *srcPtr= (uint8*)vba.getVertexCoordPointer(i); uint8 *dstPtr= (uint8*)vbaOut.getVertexCoordPointer(vid); // Get bendWeight for this vertex. float vertexBendWeight= ((CUV*)(srcPtr + srcTex1Off))->U * bendFactor; // Pos. //------- // Separate Center and relative pos. CVector relPos= mat.mulVector(*(CVector*)srcPtr); // mulVector, because translation in v[center] // compute bendCenterPos CVector bendCenterPos; if(shape->BendCenterMode == CVegetableShapeBuild::BendCenterNull) bendCenterPos= CVector::Null; else { CVector v= *(CVector*)srcPtr; v.z= 0; bendCenterPos= mat.mulVector(v); // mulVector, because translation in v[center] } // copy deltaPos= relPos-bendCenterPos; *(CVector*)dstPtr= deltaPos; *(CVector*)(dstPtr + dstCenterOff)= instancePos + bendCenterPos; // if !destLighted, then VP is different if(!destLighted) { deltaPosNorm= deltaPos.norm(); // copy bendWeight in v.w CVectorH *vh= (CVectorH*)dstPtr; // Mul by deltaPosNorm, to draw an arc circle. vh->w= vertexBendWeight * deltaPosNorm; } // Enlarge the clipBlock of the IG. // Since small shape, enlarge with each vertices. simpler and maybe faster. // TODO_VEGET: bend and clipping ... ig->_ClipOwner->extendBBoxOnly(instancePos + relPos); // prepare for ZSort if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) { worldVertices[i]= instancePos + relPos; } // Color-ligthing. //------- if(!precomputeLighting) { // just copy the primary color (means diffuse part if lighted) *(CRGBA*)(dstPtr + dstColor0Off)= primaryRGBA; // normal and secondary color if(destLighted) { // normal *(CVector*)(dstPtr + dstNormalOff)= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) ); } // If destLighted, secondaryRGBA is the ambient // else secondaryRGBA is used only for Alpha (DLM uv.v). *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA; } else { nlassert(!destLighted); // compute normal. CVector rotNormal= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) ); // must normalize() because scale is possible. rotNormal.normalize(); // Do the compute. if(!bestSidedPrecomputeLighting) { computeVegetVertexLighting(rotNormal, _DirectionalLight, primaryRGBA, secondaryRGBA, vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) ); } else { computeVegetVertexLightingForceBestSided(rotNormal, _DirectionalLight, primaryRGBA, secondaryRGBA, vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) ); } // copy secondaryRGBA, used only for Alpha (DLM uv.v). *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA; } // Texture. //------- *(CUV*)(dstPtr + dstTex0Off)= *(CUV*)(srcPtr + srcTex0Off); // Bend. //------- CVector *dstBendPtr= (CVector*)(dstPtr + dstBendOff); // setup bend Phase. dstBendPtr->y= bendPhase; // setup bend Weight. // if !destLighted, then VP is different, vertexBendWeight is stored in v[0].w if(destLighted) dstBendPtr->x= vertexBendWeight; else // the VP need the norm of relPos in v[9].x dstBendPtr->x= deltaPosNorm; // setup bendFreqFactor dstBendPtr->z= bendFreqFactor; /// If AlphaBlend / ZSort rdrPass, then setup AlphaBlend computing. if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) { // get ptr on v[9].w NB: in Unlit mode, it has 4 components. CVectorH *dstBendPtr= (CVectorH*)(dstPtr + dstBendOff); // setup the constant of linear formula: // Alpha= -1/blendTransDist * dist + blendDistMax/blendTransDist dstBendPtr->w= blendDistMax/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST; } // fill the vertex in AGP. //------- allocator->flushVertex(vid); } // must recompute the sphere according to the bbox. ig->_ClipOwner->updateSphere(); // If ZSort, compute Triangle Centers and Orders for quadrant //-------------------- if(rdrPass==NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) { // inform the SB that it must be updated. ig->_SortOwner->_Dirty= true; // For deletion, inform the ig that it has instances which impact the SB. ig->_HasZSortPassInstances= true; // change UnderWater falg of the SB if(vegetWaterState == AboveWater) ig->_SortOwner->_UnderWater= false; else if(vegetWaterState == UnderWater) ig->_SortOwner->_UnderWater= true; // static to avoid reallocation static vector triangleCenters; triangleCenters.resize(numNewTris); // compute triangle centers for(uint i=0; iTriangleIndices[i*3+0]; uint v1= shape->TriangleIndices[i*3+1]; uint v2= shape->TriangleIndices[i*3+2]; // get world coord. const CVector &vert0= worldVertices[v0]; const CVector &vert1= worldVertices[v1]; const CVector &vert2= worldVertices[v2]; // compute center triangleCenters[i]= (vert0 + vert1 + vert2) / 3; // relative to center of the sortBlock (for sint16 compression) triangleCenters[i]-= ig->_SortOwner->_Center; } // resize the array. Actually only modify the number of triangles really setuped. uint offTri= ig->_TriangleQuadrantOrderNumTriangles; ig->_TriangleQuadrantOrderNumTriangles+= numNewTris; // verify user has correclty used reserveIg system. nlassert(ig->_TriangleQuadrantOrderNumTriangles * NL3D_VEGETABLE_NUM_QUADRANT <= ig->_TriangleQuadrantOrderArray.size()); // compute distance for each quadrant. Since we are not sure of the sortBlockSize, mul with a (big: 16) security. // NB: for landscape practical usage, this left us with more than 1mm precision. float distFactor=32768/(16*ig->_SortOwner->_Radius); for(uint quadId=0; quadId_TriangleQuadrantOrders[quadId][offTri + i]= (sint16)NLMISC::OptFastFloor(dist*distFactor); } } } // Append list of indices and list of triangles to the IG //-------------------- // TODO_VEGET_OPTIM: system reallocation of array is very bad... // compute dest start idx. uint offVertex= vegetRdrPass.NVertices; uint offTri= vegetRdrPass.NTriangles; uint offTriIdx= offTri*3; // verify user has correclty used reserveIg system. nlassert(offVertex + numNewVertices <= vegetRdrPass.Vertices.size()); nlassert(offTriIdx + numNewIndices <= vegetRdrPass.TriangleIndices.getNumIndexes()); nlassert(offTriIdx + numNewIndices <= vegetRdrPass.TriangleLocalIndices.size()); // insert list of vertices to delete in ig vertices. vegetRdrPass.Vertices.copy(offVertex, offVertex+numNewVertices, &shape->InstanceVertices[0]); // insert array of triangles in ig. // for all indices, fill IG CIndexBufferReadWrite ibaWrite; vegetRdrPass.TriangleIndices.lock (ibaWrite); if (vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices16) { uint16 *ptr = (uint16 *) ibaWrite.getPtr(); for(i=0; i<(sint)numNewIndices; i++) { // get the index of the vertex in the shape uint vid= shape->TriangleIndices[i]; // re-direction, using InstanceVertices; #ifdef NL_DEBUG nlassert(shape->InstanceVertices[vid] <= 0xffff); #endif ptr[offTriIdx + i]= (uint16) shape->InstanceVertices[vid]; // local re-direction: adding vertexOffset. vegetRdrPass.TriangleLocalIndices[offTriIdx + i]= offVertex + vid; } } else { uint32 *ptr = (uint32 *) ibaWrite.getPtr(); for(i=0; i<(sint)numNewIndices; i++) { // get the index of the vertex in the shape uint vid= shape->TriangleIndices[i]; // re-direction, using InstanceVertices; ptr[offTriIdx + i]= shape->InstanceVertices[vid]; // local re-direction: adding vertexOffset. vegetRdrPass.TriangleLocalIndices[offTriIdx + i]= offVertex + vid; } } // new triangle and vertex size. vegetRdrPass.NTriangles+= numNewTris; vegetRdrPass.NVertices+= numNewVertices; // if lighted, must add a lightedInstance for lighting update. //-------------------- if(instanceLighted) { // first, update Ig. ig->_ULNumVertices+= numNewVertices; // and update the vegetable manager. _ULNTotalVertices+= numNewVertices; // link at the end of the circular list: link before the current root. if(_ULRootIg==NULL) _ULRootIg= ig; else ig->linkBeforeUL(_ULRootIg); // check good use of reserveIg. nlassert(vegetRdrPass.NLightedInstances < vegetRdrPass.LightedInstances.size()); // Fill instance info CVegetableInstanceGroup::CVegetableLightedInstance &vli= vegetRdrPass.LightedInstances[vegetRdrPass.NLightedInstances]; vli.Shape= shape; vli.NormalMat= normalMat; // copy colors unmodulated by global light. vli.MatAmbient= ambientRGBA; vli.MatDiffuse= diffuseRGBA; // store dynamic lightmap UV vli.DlmUV= dlmUV; // where vertices of this instances are wrote in the VegetRdrPass vli.StartIdInRdrPass= offVertex; // Inc size setuped. vegetRdrPass.NLightedInstances++; } } // *************************************************************************** void CVegetableManager::swapIgRdrPassHardMode(CVegetableInstanceGroup *ig, uint rdrPass) { CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ig->_RdrPass[rdrPass]; // the allocator where vertices come from CVegetableVBAllocator &srcAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vegetRdrPass.HardMode); // the allocator where vertices will go CVegetableVBAllocator &dstAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, !vegetRdrPass.HardMode); // vertex size uint vbSize= srcAllocator.getSoftwareVertexBuffer().getVertexSize(); nlassert(vbSize == dstAllocator.getSoftwareVertexBuffer().getVertexSize()); CVertexBufferRead vbaIn; srcAllocator.getSoftwareVertexBuffer ().lock(vbaIn); // for all vertices of the IG, change of VBAllocator uint i; // Do it only for current Vertices setuped!!! because a swapIgRdrPassHardMode awlays arise when the ig is // in construcion. // Hence here, we may have vegetRdrPass.NVertices < vegetRdrPass.Vertices.size() !!! for(i=0;isetFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); tex->setWrapS(ITexture::Clamp); tex->setWrapT(ITexture::Clamp); } // *************************************************************************** void CVegetableManager::loadTexture(ITexture *itex) { // setup a ITexture (smartPtr-ized). // Store in stage1, for dynamicLightmaping _VegetableMaterial.setTexture(1, itex); } // *************************************************************************** void CVegetableManager::setDirectionalLight(const CRGBA &ambient, const CRGBA &diffuse, const CVector &light) { _DirectionalLight= light; _DirectionalLight.normalize(); // Setup ambient/Diffuse. _GlobalAmbient= ambient; _GlobalDiffuse= diffuse; } // *************************************************************************** void CVegetableManager::lockBuffers() { // lock all buffers for(uint i=0; i _SortKey>o.Sb->_SortKey; } }; // *************************************************************************** void CVegetableManager::setupVertexProgramConstants(IDriver *driver) { // Standard // setup VertexProgram constants. // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix(); driver->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); // c[6] take the Fog vector. After setupModelMatrix(); driver->setConstantFog(6); // c[8] take useful constants. driver->setConstant(8, 0, 1, 0.5f, 2); // c[9] take normalized directional light driver->setConstant(9, _DirectionalLight); // c[10] take pos of camera driver->setConstant(10, _ViewCenter); // c[11] take factor for Blend formula driver->setConstant(11, -1.f/NL3D_VEGETABLE_BLOCK_BLEND_TRANSITION_DIST, 0, 0, 0); // Bend. // c[16]= quaternion axis. w==1, and z must be 0 driver->setConstant( 16, _AngleAxis.x, _AngleAxis.y, _AngleAxis.z, 1); // c[17]= {timeAnim, WindPower, WindPower*(1-WindBendMin)/2, 0)} driver->setConstant( 17, (float)_WindAnimTime, _WindPower, _WindPower*(1-_WindBendMin)/2, 0 ); // c[18]= High order Taylor cos coefficient: { -1/2, 1/24, -1/720, 1/40320 } driver->setConstant( 18, -1/2.f, 1/24.f, -1/720.f, 1/40320.f ); // c[19]= Low order Taylor cos coefficient: { 1, -1/2, 1/24, -1/720 } driver->setConstant( 19, 1, -1/2.f, 1/24.f, -1/720.f ); // c[20]= Low order Taylor sin coefficient: { 1, -1/6, 1/120, -1/5040 } driver->setConstant( 20, 1, -1/6.f, 1/120.f, -1/5040.f ); // c[21]= Special constant vector for quatToMatrix: { 0, 1, -1, 0 } driver->setConstant( 21, 0.f, 1.f, -1.f, 0.f); // c[22]= {0.5f, Pi, 2*Pi, 1/(2*Pi)} driver->setConstant( 22, 0.5f, (float)Pi, (float)(2*Pi), (float)(1/(2*Pi)) ); // c[23]= {NL3D_VEGETABLE_VP_LUT_SIZE, 0, 0, 0}. NL3D_VEGETABLE_VP_LUT_SIZE==64. driver->setConstant( 23, NL3D_VEGETABLE_VP_LUT_SIZE, 0.f, 0.f, 0.f ); // Fill constant. Start at 32. for(uint i=0; isetConstant( 32+i, cur.x, cur.y, delta.x, delta.y ); } } // *************************************************************************** void CVegetableManager::render(const CVector &viewCenter, const CVector &frontVector, const std::vector &pyramid, ITexture *textureDLM, IDriver *driver) { H_AUTO( NL3D_Vegetable_Render ); CVegetableClipBlock *rootToRender= NULL; // get normalized front vector. CVector frontVectorNormed= frontVector.normed(); // For Speed debug only. /*extern bool YOYO_ATTest; if(YOYO_ATTest) return; */ // Clip. //-------------------- // For all current not empty clipBlocks, clip against pyramid, and insert visibles in list. CVegetableClipBlock *ptrClipBlock= _ClipBlockList.begin(); while(ptrClipBlock) { // if the clipBlock is visible and not empty if(ptrClipBlock->clip(pyramid)) { // insert into visible list. ptrClipBlock->_RenderNext= rootToRender; rootToRender= ptrClipBlock; } // next ptrClipBlock= (CVegetableClipBlock*)ptrClipBlock->Next; } // If no clip block visible, just skip!! if(rootToRender==NULL) return; // Prepare Render //-------------------- // profile. CPrimitiveProfile ppIn, ppOut; driver->profileRenderedPrimitives(ppIn, ppOut); uint precNTriRdr= ppOut.NTriangles; // Disable Fog. bool bkupFog; bkupFog= driver->fogEnabled(); bool fogged = bkupFog && driver->getFogStart() < _ZSortLayerDistMax; driver->enableFog(fogged); // Used by setupVertexProgramConstants(). The center of camera. // Used for AlphaBlending, and for ZBuffer precision problems. _ViewCenter= viewCenter; // The manager is identity in essence. But for ZBuffer improvements, must set it as close // to the camera. In the VertexProgram, _ViewCenter is substracted from bent vertex pos. So take it as position. _ManagerMatrix.identity(); _ManagerMatrix.setPos(_ViewCenter); // set model matrix to the manager matrix. driver->setupModelMatrix(_ManagerMatrix); // set the driver for all allocators updateDriver(driver); // Compute Bend Anim. // AnimFrequency factor. // Doing it incrementally allow change of of frequency each frame with good results. _WindAnimTime+= (_Time - _WindPrecRenderTime)*_WindFrequency; _WindAnimTime= fmod((float)_WindAnimTime, (float)NL3D_VEGETABLE_FREQUENCY_FACTOR_PREC); // NB: Leave timeBend (_WindAnimTime) as a time (ie [0..1]), because VP do a "EXP time". // For incremental computing. _WindPrecRenderTime= _Time; // compute the angleAxis corresponding to direction // perform a 90deg rotation to get correct angleAxis _AngleAxis.set(-_WindDirection.y,_WindDirection.x,0); // Fill LUT WindTable. uint i; for(i=0; i=0; vbHardMode--) { // For all renderPass. for(sint rdrPass=0; rdrPass < NL3D_VEGETABLE_NRDRPASS; rdrPass++) { // skip ZSORT rdrPass, done after. if(rdrPass == NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT) continue; // which allocator? CVegetableVBAllocator &vbAllocator= getVBAllocatorForRdrPassAndVBHardMode(rdrPass, vbHardMode); // Do the pass only if there is some vertices to draw. if(vbAllocator.getNumUserVerticesAllocated()>0) { // additional setup to the material bool doubleSided= doubleSidedRdrPass(rdrPass); // set the 2Sided flag in the material _VegetableMaterial.setDoubleSided( doubleSided ); // Activate the unique material. driver->setupMaterial(_VegetableMaterial); // activate Vertex program first. //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str()); nlverify(driver->activeVertexProgram(_VertexProgram[rdrPass][fogged ? 1 : 0])); // Activate the good VBuffer vbAllocator.activate(); // For all visibles clipBlock, render their instance groups. ptrClipBlock= rootToRender; while(ptrClipBlock) { // For all sortBlock of the clipBlock CVegetableSortBlock *ptrSortBlock= ptrClipBlock->_SortBlockList.begin(); while(ptrSortBlock) { // For all igs of the sortBlock CVegetableInstanceGroup *ptrIg= ptrSortBlock->_InstanceGroupList.begin(); while(ptrIg) { // rdrPass CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= ptrIg->_RdrPass[rdrPass]; // if this rdrPass is in same HardMode as we process now. if( (vegetRdrPass.HardMode && vbHardMode==1) || (!vegetRdrPass.HardMode && vbHardMode==0) ) { // Ok, Render the faces. if(vegetRdrPass.NTriangles) { driver->activeIndexBuffer(vegetRdrPass.TriangleIndices); #ifdef NL_DEBUG if (vegetRdrPass.HardMode) { nlassert(vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices16); } else { nlassert(vegetRdrPass.TriangleIndices.getFormat() == CIndexBuffer::Indices32); } #endif driver->renderSimpleTriangles(0, vegetRdrPass.NTriangles); } } // next ig. ptrIg= (CVegetableInstanceGroup*)ptrIg->Next; } // next sortBlock ptrSortBlock= (CVegetableSortBlock *)(ptrSortBlock->Next); } // next clipBlock to render ptrClipBlock= ptrClipBlock->_RenderNext; } } } } // Render ZSort pass. //-------------------- // Debug Quadrants. /*static vector p0DebugLines; static vector p1DebugLines; p0DebugLines.clear(); p1DebugLines.clear();*/ // For all Blend model Layers, clear Sort Block list and setup. for(i=0; i<_NumZSortBlendLayers;i++) { // must have been created. nlassert(_ZSortModelLayers[i]); nlassert(_ZSortModelLayersUW[i]); // NB: don't refresh list, it is done in CVegetableBlendLayerModel. // We must do it here, because if vegetableManger::render() is no more called (eg: disabled), // then the models must do nothing. // To get layers correclty sorted from fornt to back, must init their pos // because it is the renderTraversal which sort them. // compute distance to camera of this layer. float layerZ= i * _ZSortLayerDistMax / _NumZSortBlendLayers; // compute position of this layer. CVector pos= viewCenter + frontVector * layerZ; // special setup in the layer. _ZSortModelLayers[i]->setWorldPos(pos); _ZSortModelLayersUW[i]->setWorldPos(pos); } // If some vertices in arrays for ZSort rdrPass if( getVBAllocatorForRdrPassAndVBHardMode(NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT, 0).getNumUserVerticesAllocated()>0 || getVBAllocatorForRdrPassAndVBHardMode(NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT, 1).getNumUserVerticesAllocated()>0 ) { uint rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT; // sort //------------- // Array for sorting. (static to avoid reallocation) static vector sortVegetSbs; sortVegetSbs.clear(); // For all visibles clipBlock ptrClipBlock= rootToRender; while(ptrClipBlock) { // For all sortBlock, prepare to sort them CVegetableSortBlock *ptrSortBlock= ptrClipBlock->_SortBlockList.begin(); while(ptrSortBlock) { // if the sortBlock has some sorted faces to render if(ptrSortBlock->_NTriangles != 0) { // Compute Distance to Viewer. /* NB: compute radial distance (with norm()) instead of linear distance (DotProduct with front vector) get less "ZSort poping". */ CVector dirToSb= ptrSortBlock->_Center - viewCenter; float distToViewer= dirToSb.norm(); // SortKey change if the center is behind the camera. if(dirToSb * frontVectorNormed<0) { ptrSortBlock->_SortKey= - distToViewer; } else { ptrSortBlock->_SortKey= distToViewer; } // Choose the quadrant for this sortBlock sint bestDirIdx= 0; float bestDirVal= -FLT_MAX; // If too near, must take the frontVector as key, to get better sort. // use ptrSortBlock->_SortKey to get correct negative values. if(ptrSortBlock->_SortKey < ptrSortBlock->_Radius) { dirToSb= frontVectorNormed; } // NB: no need to normalize dirToSb, because need only to sort with DP // choose the good list of triangles according to quadrant. for(uint dirIdx=0; dirIdxbestDirVal) { bestDirVal= dirVal; bestDirIdx= dirIdx; } } // set the result. ptrSortBlock->_QuadrantId= bestDirIdx; // insert in list to sort. sortVegetSbs.push_back(CSortVSB(ptrSortBlock)); // Debug Quadrants /*p0DebugLines.push_back(ptrSortBlock->_Center); p1DebugLines.push_back(ptrSortBlock->_Center + CVegetableQuadrant::Dirs[bestDirIdx]);*/ } // next sortBlock ptrSortBlock= (CVegetableSortBlock *)(ptrSortBlock->Next); } // next clipBlock to render ptrClipBlock= ptrClipBlock->_RenderNext; } // sort! // QSort. (I tried, better than radix sort, guckk!!) sort(sortVegetSbs.begin(), sortVegetSbs.end()); // setup material for this rdrPass. NB: rendered after (in LayerModels). //------------- bool doubleSided= doubleSidedRdrPass(rdrPass); // set the 2Sided flag in the material _VegetableMaterial.setDoubleSided( doubleSided ); // setup the unique material. _VegetableMaterial.setBlend(true); _VegetableMaterial.setZWrite(false); // leave AlphaTest but still kick low alpha values (for fillRate performance) _VegetableMaterial.setAlphaTestThreshold(0.1f); // order them in Layers. //------------- // render from back to front, to keep correct Z order in a single layer. for(uint i=0; i_SortKey; // compute in which layer must store this SB. z= z*_NumZSortBlendLayers / _ZSortLayerDistMax; // Avoid a floor(), using an OptFastFloor, but without the OptFastFloorBegin() End() group. // => avoid the imprecision with such a trick; *256, then divide the integer by 256. sint layer= NLMISC::OptFastFloor(z*256) >> 8; clamp(layer, 0, (sint)_NumZSortBlendLayers-1); // Range in correct layer, according to water ordering if(ptrSortBlock->_UnderWater) // range in the correct layermodel (NB: keep the same layer internal order). _ZSortModelLayersUW[layer]->SortBlocks.push_back(ptrSortBlock); else _ZSortModelLayers[layer]->SortBlocks.push_back(ptrSortBlock); } } // Quit //-------------------- // disable VertexProgram. driver->activeVertexProgram(NULL); // restore Fog. driver->enableFog(bkupFog); // Debug Quadrants /*for(uint l=0; lprofileRenderedPrimitives(ppIn, ppOut); _NumVegetableFaceRendered= ppOut.NTriangles-precNTriRdr; } // *************************************************************************** void CVegetableManager::setupRenderStateForBlendLayerModel(IDriver *driver) { // Setup Global. //============= // disable fog, for faster VP. _BkupFog= driver->fogEnabled(); static volatile bool testDist = true; bool fogged = _BkupFog && driver->getFogStart() < _ZSortLayerDistMax; driver->enableFog(fogged); // set model matrix to the manager matrix. driver->setupModelMatrix(_ManagerMatrix); // setup VP constants. setupVertexProgramConstants(driver); // Setup RdrPass. //============= uint rdrPass= NL3D_VEGETABLE_RDRPASS_UNLIT_2SIDED_ZSORT; // Activate the unique material (correclty setuped for AlphaBlend in render()). driver->setupMaterial(_VegetableMaterial); // activate Vertex program first. //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[rdrPass]->getProgram().c_str()); nlverify(driver->activeVertexProgram(_VertexProgram[rdrPass][fogged ? 1 : 0])); if (fogged) { driver->setConstantFog(6); } } // *************************************************************************** void CVegetableManager::resetNumVegetableFaceRendered() { _NumVegetableFaceRendered= 0; } // *************************************************************************** uint CVegetableManager::getNumVegetableFaceRendered() const { return _NumVegetableFaceRendered; } // *************************************************************************** void CVegetableManager::exitRenderStateForBlendLayerModel(IDriver *driver) { // disable VertexProgram. driver->activeVertexProgram(NULL); // restore Fog. driver->enableFog(_BkupFog); } // *************************************************************************** void CVegetableManager::setWind(const CVector &windDir, float windFreq, float windPower, float windBendMin) { // Keep only XY component of the Wind direction (because VP only support z==0 quaternions). _WindDirection= windDir; _WindDirection.z= 0; _WindDirection.normalize(); // copy setup _WindFrequency= windFreq; _WindPower= windPower; _WindBendMin= windBendMin; clamp(_WindBendMin, 0, 1); } // *************************************************************************** void CVegetableManager::setTime(double time) { // copy time _Time= time; } // *************************************************************************** // *************************************************************************** // Lighting part. // *************************************************************************** // *************************************************************************** // *************************************************************************** void CVegetableManager::setUpdateLightingTime(double time) { _ULTime= time; } // *************************************************************************** void CVegetableManager::updateLighting() { // first time in this method?? if(!_ULPrecTimeInit) { _ULPrecTimeInit= true; _ULPrecTime= _ULTime; } // compute delta time from last update. float dt= float(_ULTime - _ULPrecTime); _ULPrecTime= _ULTime; // compute number of vertices to update. _ULNVerticesToUpdate+= dt*_ULFrequency * _ULNTotalVertices; // maximize, so at max, it computes all Igs, just one time. _ULNVerticesToUpdate= min(_ULNVerticesToUpdate, (float)_ULNTotalVertices); // go. doUpdateLighting(); } // *************************************************************************** void CVegetableManager::updateLightingAll() { // maximize, so at max, it computes all Igs _ULNVerticesToUpdate= (float)_ULNTotalVertices; // go. doUpdateLighting(); } // *************************************************************************** void CVegetableManager::doUpdateLighting() { // while there is still some vertices to update. while(_ULNVerticesToUpdate > 0 && _ULRootIg) { // update the current ig. if all updated, skip to next one. if(updateLightingIGPart()) { // next _ULRootIg= _ULRootIg->_ULNext; } } // Now, _ULNVerticesToUpdate should be <=0. (most of the time < 0) } // *************************************************************************** void CVegetableManager::setUpdateLightingFrequency(float freq) { freq= max(freq, 0.f); _ULFrequency= freq; } // *************************************************************************** bool CVegetableManager::updateLightingIGPart() { nlassert(_ULRootIg); // First, update lighting info global to the ig, ie update current // colros of the PointLights which influence the ig. _ULRootIg->VegetableLightEx.computeCurrentColors(); // while there is some vertices to update while(_ULNVerticesToUpdate>0) { // if all rdrPass of the ig are processed. if(_ULCurrentIgRdrPass>= NL3D_VEGETABLE_NRDRPASS) { // All this Ig is updated. _ULCurrentIgRdrPass= 0; _ULCurrentIgInstance= 0; // skip to next Ig. return true; } CVegetableInstanceGroup::CVegetableRdrPass &vegetRdrPass= _ULRootIg->_RdrPass[_ULCurrentIgRdrPass]; // if all instances are processed for this pass (especially if size()==0 !!) if(_ULCurrentIgInstance>= vegetRdrPass.LightedInstances.size()) { // skip to the next rdrPass. _ULCurrentIgRdrPass++; _ULCurrentIgInstance= 0; continue; } // Process this instance. _ULNVerticesToUpdate-= updateInstanceLighting(_ULRootIg, _ULCurrentIgRdrPass, _ULCurrentIgInstance); // next instance. _ULCurrentIgInstance++; // if all instances are processed for this pass if(_ULCurrentIgInstance>= vegetRdrPass.LightedInstances.size()) { // skip to the next rdrPass. _ULCurrentIgRdrPass++; _ULCurrentIgInstance= 0; } } // If all rdrPass of the ig are processed. if(_ULCurrentIgRdrPass>= NL3D_VEGETABLE_NRDRPASS) { // All this Ig is updated. _ULCurrentIgRdrPass= 0; _ULCurrentIgInstance= 0; // skip to next Ig. return true; } else { // The Ig is not entirely updated. return false; } } // *************************************************************************** uint CVegetableManager::updateInstanceLighting(CVegetableInstanceGroup *ig, uint rdrPassId, uint instanceId) { nlassert(ig); // get the rdrPass. nlassert(rdrPassId_RdrPass[rdrPassId]; // get the lighted instance. nlassert(instanceIdLighted); bool instanceLighted= true; // get ref on the vegetLex. CVegetableLightEx &vegetLex= ig->VegetableLightEx; // Color of pointLights modulated by diffuse. CRGBA diffusePL[2]; if(vegetLex.NumLights>=1) { diffusePL[0].modulateFromColorRGBOnly(vegetLI.MatDiffuse, vegetLex.Color[0]); if(vegetLex.NumLights>=2) { diffusePL[1].modulateFromColorRGBOnly(vegetLI.MatDiffuse, vegetLex.Color[1]); } } // Recompute lighting //=========== // setup for this instance. //--------- // Precompute lighting or not?? bool precomputeLighting= instanceLighted && shape->PreComputeLighting; // bestSided Precompute lighting or not?? bool bestSidedPrecomputeLighting= precomputeLighting && shape->BestSidedPreComputeLighting; // destLighted? bool destLighted= instanceLighted && !shape->PreComputeLighting; // Diffuse and ambient, modulated by current GlobalAmbient and GlobalDiffuse. CRGBA primaryRGBA, secondaryRGBA; primaryRGBA.modulateFromColorRGBOnly(vegetLI.MatDiffuse, _GlobalDiffuse); secondaryRGBA.modulateFromColorRGBOnly(vegetLI.MatAmbient, _GlobalAmbient); // get normal matrix CMatrix &normalMat= vegetLI.NormalMat; // array of vertex id to update uint32 *ptrVid= vegetRdrPass.Vertices.getPtr() + vegetLI.StartIdInRdrPass; uint numVertices= shape->InstanceVertices.size(); // Copy Dynamic Lightmap UV in Alpha part (save memory for an extra cost of 1 VP instruction) primaryRGBA.A= vegetLI.DlmUV.U; secondaryRGBA.A= vegetLI.DlmUV.V; // get VertexBuffer info. CVegetableVBAllocator *allocator; allocator= &getVBAllocatorForRdrPassAndVBHardMode(rdrPassId, vegetRdrPass.HardMode); const CVertexBuffer &dstVBInfo= allocator->getSoftwareVertexBuffer(); uint srcNormalOff= (instanceLighted? shape->VB.getNormalOff() : 0); uint dstColor0Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR0); uint dstColor1Off= dstVBInfo.getValueOffEx(NL3D_VEGETABLE_VPPOS_COLOR1); // For D3D, If the VertexBuffer is in BGRA mode if(allocator->isBGRA()) { // then swap only the B and R (no cpu cycle added per vertex) primaryRGBA.swapBR(); secondaryRGBA.swapBR(); diffusePL[0].swapBR(); diffusePL[1].swapBR(); } CVertexBufferRead vba; shape->VB.lock (vba); CVertexBufferReadWrite vbaOut; allocator->getSoftwareVertexBuffer ().lock(vbaOut); // For all vertices, recompute lighting. //--------- for(sint i=0; i<(sint)numVertices;i++) { // get the Vertex in the VB. uint vid= ptrVid[i]; // store in tmp shape. shape->InstanceVertices[i]= vid; // Fill this vertex. const uint8 *srcPtr= (const uint8*)vba.getVertexCoordPointer(i); uint8 *dstPtr= (uint8*)vbaOut.getVertexCoordPointer(vid); // if !precomputeLighting (means destLighted...) if(!precomputeLighting) { // just copy the primary and secondary color *(CRGBA*)(dstPtr + dstColor0Off)= primaryRGBA; *(CRGBA*)(dstPtr + dstColor1Off)= secondaryRGBA; } else { nlassert(!destLighted); // compute normal. CVector rotNormal= normalMat.mulVector( *(CVector*)(srcPtr + srcNormalOff) ); // must normalize() because scale is possible. rotNormal.normalize(); // Do the compute. if(!bestSidedPrecomputeLighting) { computeVegetVertexLighting(rotNormal, _DirectionalLight, primaryRGBA, secondaryRGBA, vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) ); } else { computeVegetVertexLightingForceBestSided(rotNormal, _DirectionalLight, primaryRGBA, secondaryRGBA, vegetLex, diffusePL, (CRGBA*)(dstPtr + dstColor0Off) ); } } // flust the vertex in AGP. allocator->flushVertex(vid); } // numVertices vertices are updated return numVertices; } } // NL3D