// 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;
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