khanat-opennel-code/code/nel/src/3d/quad_grid_clip_manager.cpp

391 lines
10 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/quad_grid_clip_manager.h"
#include "nel/3d/scene.h"
#include "nel/3d/transform_shape.h"
#include "nel/3d/clip_trav.h"
#include "nel/misc/aabbox.h"
#include "nel/3d/cluster.h"
#include "nel/misc/hierarchical_timer.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
void CQuadGridClipManager::registerBasic()
{
CScene::registerModel(QuadGridClipManagerId, TransformId, CQuadGridClipManager::creator);
}
// ***************************************************************************
CQuadGridClipManager::CQuadGridClipManager()
{
_ClusterSize= 0;
_X= _Y= 0;
_Width= _Height= 0;
}
// ***************************************************************************
CQuadGridClipManager::~CQuadGridClipManager()
{
reset();
}
// ***************************************************************************
void CQuadGridClipManager::init(float clusterSize, uint numDist, float maxDist, float radiusMax )
{
// reset first.
reset();
// copy params.
nlassert(clusterSize>0);
_ClusterSize= clusterSize;
_MaxDist= maxDist;
_NumDist= numDist;
_RadiusMax= radiusMax;
}
// ***************************************************************************
void CQuadGridClipManager::reset()
{
// delete the clusters.
if(getOwnerScene())
{
sint oldX0, oldX1, oldY0, oldY1;
oldX0= _X;
oldX1= _X+_Width;
oldY0= _Y;
oldY1= _Y+_Height;
for(sint y=oldY0; y<oldY1; y++)
{
for(sint x=oldX0; x<oldX1; x++)
{
deleteCaseModels(&getOwnerScene()->getClipTrav(), x,y);
}
}
// clear the grid.
_QuadGridClusterCases.clear();
}
// reset others params.
_ClusterSize= 0;
_X= _Y= 0;
_Width= _Height= 0;
}
// ***************************************************************************
void CQuadGridClipManager::updateClustersFromCamera(const CVector &camPos)
{
H_AUTO( NL3D_QuadClip_updateClusters );
CClipTrav *pClipTrav= &getOwnerScene()->getClipTrav();
sint newX0, newX1, newY0, newY1;
sint oldX0, oldX1, oldY0, oldY1;
oldX0= _X;
oldX1= _X+_Width;
oldY0= _Y;
oldY1= _Y+_Height;
// compute the new square of clusters to build.
newX0= (sint)floor( (camPos.x - _RadiusMax) / _ClusterSize);
newX1= (sint)ceil( (camPos.x + _RadiusMax) / _ClusterSize);
newY0= (sint)floor( (camPos.y - _RadiusMax) / _ClusterSize);
newY1= (sint)ceil( (camPos.y + _RadiusMax) / _ClusterSize);
// keep an histeresis of one cluster: do not delete "young" clusters created before.
if(newX0>= oldX0+1) // NB: if newX0==oldX0+1, then newX0= _X => no change.
newX0--;
if(newY0>= oldY0+1) // same reasoning.
newY0--;
if(newX1<= oldX1-1) // NB: if newX1==oldX1-1, then newX1= oldX1 => no change.
newX1++;
if(newY1<= oldY1-1) // same reasoning.
newY1++;
// Have we got to update the array.
if(newX0!=oldX0 || newX1!=oldX1 || newY0!=oldY0 || newY1!=oldY1)
{
sint x,y;
// delete olds models.
// simple: test all cases
for(y=oldY0; y<oldY1; y++)
{
for(x=oldX0; x<oldX1; x++)
{
// if out the new square?
if(x<newX0 || x>=newX1 || y<newY0 || y>=newY1)
deleteCaseModels(pClipTrav, x,y);
}
}
// build new array
// satic for alloc optimisation.
static vector<CQuadGridClipCluster*> newQuadGridClusterCases;
sint newWidth= newX1-newX0;
sint newHeight= newY1-newY0;
newQuadGridClusterCases.resize(newWidth * newHeight);
// simple: test all cases
for(y=newY0; y<newY1; y++)
{
for(x=newX0; x<newX1; x++)
{
CQuadGridClipCluster *&newCase= newQuadGridClusterCases[ (y-newY0)*newWidth + (x-newX0) ];
// if out the old square?
if(x<oldX0 || x>=oldX1 || y<oldY0 || y>=oldY1)
{
// build the 2D bbox where the models should fit.
CAABBox pivotBBox;
pivotBBox.setMinMax(
CVector(x*_ClusterSize,y*_ClusterSize,0),
CVector((x+1)*_ClusterSize,(y+1)*_ClusterSize,0) );
// build new case. Clusters are empty.
newCaseModels(newCase, pivotBBox);
}
else
{
// copy from old.
CQuadGridClipCluster *oldCase= _QuadGridClusterCases[ (y-_Y)*_Width + (x-_X) ];
newCase= oldCase;
}
}
}
// just copy from new.
_QuadGridClusterCases= newQuadGridClusterCases;
_X= newX0;
_Y= newY0;
_Width= newWidth;
_Height= newHeight;
}
}
// ***************************************************************************
bool CQuadGridClipManager::linkModel(CTransformShape *pTfmShp)
{
H_AUTO( NL3D_QuadClip_linkModel );
// use the position to get the cluster to use.
CAABBox box;
pTfmShp->getAABBox (box);
// Transform the box in the world
const CMatrix &wm = pTfmShp->getWorldMatrix();
// compute center in world.
CVector cLocal = box.getCenter();
CVector cWorld = wm * cLocal;
// prepare bbox.
CAABBox worldBBox;
worldBBox.setCenter(cWorld);
CVector hs = box.getHalfSize();
// For 8 corners.
for(uint i=0;i<8;i++)
{
CVector corner;
// compute the corner of the bbox.
corner= cLocal;
if(i&1) corner.x+=hs.x;
else corner.x-=hs.x;
if((i/2)&1) corner.y+=hs.y;
else corner.y-=hs.y;
if((i/4)&1) corner.z+=hs.z;
else corner.z-=hs.z;
// Transform the corner in world.
corner = wm * corner;
// Extend the bbox with it.
worldBBox.extend(corner);
}
// Position in the grid.
sint x,y;
x= (sint)floor( cWorld.x / _ClusterSize);
y= (sint)floor( cWorld.y / _ClusterSize);
// verify if IN the current grid of clusters created.
if( x>=_X && x<_X+_Width && y>=_Y && y<_Y+_Height )
{
// add the model and extend the bbox of this cluster.
CQuadGridClipCluster *cluster= _QuadGridClusterCases[ (y-_Y)*_Width + (x-_X) ];
// if this cluster is empty, add it now to the list of not empty (do the test before add)
if( cluster->isEmpty() )
{
_NotEmptyQuadGridClipClusters.insert(cluster, &cluster->ListNode);
}
// add the model => no more empty
cluster->addModel(worldBBox, pTfmShp);
return true;
}
else
{
return false;
}
}
// ***************************************************************************
void CQuadGridClipManager::deleteCaseModels(CClipTrav *pClipTrav, sint x, sint y)
{
H_AUTO( NL3D_QuadClip_deleteCaseModels );
nlassert(x>=_X && x<_X+_Width && y>=_Y && y<_Y+_Height);
CQuadGridClipCluster *cluster= _QuadGridClusterCases[ (y-_Y)*_Width + (x-_X) ];
// first, unlink all sons from cluster, and link thems to RootCluster.
cluster->resetSons(pClipTrav);
// delete the cluster. NB: auto-unlinked from _NotEmptyQuadGridClipClusters
delete cluster;
// NB: do not delete array for alloc/free optimisation.
}
// ***************************************************************************
void CQuadGridClipManager::newCaseModels(CQuadGridClipCluster *&cluster, const NLMISC::CAABBox &pivotBbox)
{
H_AUTO( NL3D_QuadClip_newCaseModels );
// create clusters.
cluster= new CQuadGridClipCluster(_NumDist, _MaxDist, pivotBbox);
}
// ***************************************************************************
void CQuadGridClipManager::traverseClip()
{
CClipTrav *pClipTrav= &getOwnerScene()->getClipTrav();
// Run All NotEmpty Clusters,
CQuadGridClipCluster **it;
it= _NotEmptyQuadGridClipClusters.begin();
uint numClusters= _NotEmptyQuadGridClipClusters.size();
for(;numClusters>0;numClusters--,it++)
{
(*it)->clip(pClipTrav);
}
}
// ***************************************************************************
void CQuadGridClipManager::profile() const
{
nlinfo(" ***** CQuadGridClipManager stats");
nlinfo(" There is %d clusters", _Width*_Height );
sint numEmptyClusters= 0;
float minAreaRatio= 1000;
float maxAreaRatio= 0;
float meanAreaRatio= 0;
// test all cases
for(sint y=0;y<_Height;y++)
{
for(sint x=0;x<_Width;x++)
{
const CQuadGridClipCluster *cluster= _QuadGridClusterCases[y*_Width+x];
if( cluster->isEmpty() )
numEmptyClusters++;
else
{
CAABBox bb= cluster->getBBox();
float area= (bb.getSize().x*bb.getSize().y) / (_ClusterSize*_ClusterSize);
meanAreaRatio+= area;
minAreaRatio= min( minAreaRatio, area);
maxAreaRatio= max( maxAreaRatio, area);
}
}
}
// display
if( numEmptyClusters==_Width*_Height )
{
nlinfo( " The ClipManager is completely Empty!!!!");
}
else
{
sint numClusters= _Width*_Height-numEmptyClusters;
nlinfo( " This Level has %d clusters not empty over %d", numClusters, _Width*_Height);
meanAreaRatio/= numClusters;
nlinfo(" . minAreaRatio= %f", minAreaRatio);
nlinfo(" . maxAreaRatio= %f", maxAreaRatio);
nlinfo(" . meanAreaRatio= %f", meanAreaRatio);
// Do Stats per distance setup
for(uint lvl=0;lvl<_NumDist+1;lvl++)
{
nlinfo(" * Stats for Distance up to %f m", lvl<_NumDist?(lvl+1)*_MaxDist/_NumDist:-1.0f);
sint minNumChildren= 100000000;
sint maxNumChildren= 0;
float meanNumChildren= 0;
sint totalNumChildren= 0;
// test all cases
for(sint y=0;y<_Height;y++)
{
for(sint x=0;x<_Width;x++)
{
const CQuadGridClipCluster *cluster= _QuadGridClusterCases[y*_Width+x];
if( cluster->isEmpty() )
numEmptyClusters++;
else
{
// count number of sons
sint numSons;
numSons= cluster->profileNumChildren(lvl);
meanNumChildren+= numSons;
totalNumChildren+= numSons;
minNumChildren= min( minNumChildren, numSons);
maxNumChildren= max( maxNumChildren, numSons);
}
}
}
// display
meanNumChildren/= numClusters;
nlinfo(" . minNumChildren= %d", minNumChildren);
nlinfo(" . maxNumChildren= %d", maxNumChildren);
nlinfo(" . meanNumChildren= %f", meanNumChildren);
nlinfo(" . totalNumChildren= %d", totalNumChildren);
}
}
}
} // NL3D