// 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/landscape_vegetable_block.h" #include "nel/3d/vegetable_manager.h" #include "nel/3d/vegetable_clip_block.h" #include "nel/3d/vegetable_instance_group.h" #include "nel/3d/patch.h" #include "nel/3d/bezier_patch.h" using namespace std; using namespace NLMISC; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif namespace NL3D { // *************************************************************************** /* Distances type squared. */ class CLVBSqrDistLUT { public: static float Array[NL3D_VEGETABLE_BLOCK_NUMDIST+1]; CLVBSqrDistLUT() { // 0, 10, 20, 30, 40, 50 for(uint i=0;i &vblist) { nlassert(patch); _Center= center; _VegetableClipBlock= vegetableClipBlock; _Patch= patch; _Ts= uint8(ts); _Tt= uint8(tt); // Create the Vegetable SortBlocks sint tms,tmt; // for all tiles nlassert(NL3D_TESSBLOCK_TILESIZE==4); for(tmt= _Tt; tmt<_Tt+2; tmt++) { for(tms= _Ts; tms<_Ts+2; tms++) { // compute approximate center of the tile. float s= (tms + 0.5f) / _Patch->getOrderS(); float t= (tmt + 0.5f) / _Patch->getOrderT(); CBezierPatch *bpatch= _Patch->unpackIntoCache(); CVector center= bpatch->eval(s, t); // create the sortBlock. NB: very approximate SortBlock radius.... _VegetableSortBlock[(tmt-_Tt)*2 + (tms-_Ts)]= vegetManager->createSortBlock(_VegetableClipBlock, center, NL3D_PATCH_TILE_RADIUS); } } // append to list. vblist.append(this); } // *************************************************************************** void CLandscapeVegetableBlock::release(CVegetableManager *vegeManager, CTessList &vblist) { // release all Igs, and all Sbs. for(uint j=0;jdeleteIg(_VegetableIG[j][i]); _VegetableIG[j][i]= NULL; } } // release SB if(_VegetableSortBlock[j]) { vegeManager->deleteSortBlock(_VegetableSortBlock[j]); _VegetableSortBlock[j]= NULL; } } // reset state. _CurDistType= NL3D_VEGETABLE_BLOCK_NUMDIST; // remove from list. vblist.remove(this); } // *************************************************************************** void CLandscapeVegetableBlock::update(const CVector &viewCenter, CVegetableManager *vegeManager) { float sqrDist= (viewCenter-_Center).sqrnorm(); // compute new distance type. Incremental mode. uint newDistType= _CurDistType; while(sqrDistCLVBSqrDistLUT::Array[newDistType+1]) { newDistType++; } /* NB: to test but may be better than newDistType= floor()(delta.norm() / NL3D_VEGETABLE_BLOCK_ELTDIST); */ // Change of distance type?? if(newDistType!=_CurDistType) { // Erase or create IGs. if(newDistType>_CurDistType) { // For all tiles for(uint j=0;jdeleteIg(_VegetableIG[j][i]); _VegetableIG[j][i]= NULL; } } // update the sort block for this tile _VegetableSortBlock[j]->updateSortBlock(*vegeManager); } } else { // Create a context for creation. CLandscapeVegetableBlockCreateContext ctx; ctx.init(_Patch, _Ts, _Tt); // create new Igs, for all tiles for(uint i=newDistType; i<_CurDistType; i++) { createVegetableIGForDistType(i, vegeManager, ctx); } // For all tiles for(uint j=0;jupdateSortBlock(*vegeManager); } } // copy new dist type. _CurDistType= uint8(newDistType); } } // *************************************************************************** void CLandscapeVegetableBlock::createVegetableIGForDistType(uint i, CVegetableManager *vegeManager, CLandscapeVegetableBlockCreateContext &vbCreateCtx) { // check nlassert(NL3D_TESSBLOCK_TILESIZE==4); nlassert(_VegetableIG[0][i]==NULL); nlassert(_VegetableIG[1][i]==NULL); nlassert(_VegetableIG[2][i]==NULL); nlassert(_VegetableIG[3][i]==NULL); // Create vegetables instances per tile_material. sint tms,tmt; // for all tiles nlassert(NL3D_TESSBLOCK_TILESIZE==4); for(tmt= _Tt; tmt<_Tt+2; tmt++) { for(tms= _Ts; tms<_Ts+2; tms++) { uint tileId= tms-_Ts + (tmt-_Tt)*2; // create the instance group in the good sortBlock CVegetableInstanceGroup *vegetIg= vegeManager->createIg(_VegetableSortBlock[tileId]); _VegetableIG[tileId][i]= vegetIg; // generate _Patch->generateTileVegetable(vegetIg, i, tms, tmt, vbCreateCtx); // If the ig is empty, delete him. This optimize rendering because no useless ig are // tested for rendering. This speed up some 1/10 of ms... if(vegetIg->isEmpty()) { vegeManager->deleteIg(vegetIg); _VegetableIG[tileId][i]= NULL; } // NB: vegtable SortBlock is updated in CLandscapeVegetableBlock::update() after } } } // *************************************************************************** // *************************************************************************** // CLandscapeVegetableIGCreateContext // *************************************************************************** // *************************************************************************** // *************************************************************************** CLandscapeVegetableBlockCreateContext::CLandscapeVegetableBlockCreateContext() { _Patch= NULL; } // *************************************************************************** void CLandscapeVegetableBlockCreateContext::init(CPatch *patch, uint ts, uint tt) { nlassert(patch); _Empty= true; _Patch= patch; _Ts= ts; _Tt= tt; } // *************************************************************************** void CLandscapeVegetableBlockCreateContext::eval(uint ts, uint tt, float x, float y, CVector &pos) { nlassert(NL3D_TESSBLOCK_TILESIZE==4); // If never created, do it now // ================== if(_Empty) { // Eval Position and normals for the 9 vertices (around the 2x2 tiles) for(uint j=0; j<3;j++) { float t= (float)(_Tt+j)/_Patch->getOrderT(); for(uint i=0; i<3;i++) { float s= (float)(_Ts+i)/_Patch->getOrderS(); // eval position. // use computeVertex() and not bpatch->eval() because vegetables must follow the // noise (at least at tile precision...). It is slower but necessary. _Pos[j*3+i]= _Patch->computeVertex(s, t); } } // Ok, it's done _Empty= false; } // Eval, with simple bilinear // ================== nlassert(ts==_Ts || ts==_Ts+1); nlassert(tt==_Tt || tt==_Tt+1); uint ds= ts-_Ts; uint dt= tt-_Tt; // Indices. uint v00= dt*3 + ds; uint v10= v00 + 1; uint v01= v00 + 3; uint v11= v00 + 4; // BiLinearFactor. float dxdy= (1-x)*(1-y); float dx2dy= x*(1-y); float dxdy2= (1-x)*y; float dx2dy2= x*y; // Compute Pos. pos = _Pos[v00] * dxdy; pos+= _Pos[v10] * dx2dy; pos+= _Pos[v01] * dxdy2; pos+= _Pos[v11] * dx2dy2; } } // NL3D