khanat-opennel-code/code/nel/src/3d/quad_grid_clip_cluster.cpp
2010-05-06 02:08:41 +02:00

459 lines
13 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_cluster.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/3d/transform_shape.h"
#include "nel/3d/cluster.h"
using namespace NLMISC;
using namespace std;
namespace NL3D
{
H_AUTO_DECL( NL3D_QuadClip_ClusterClip );
H_AUTO_DECL( NL3D_QuadClip_SonsShowNoClip );
// ***************************************************************************
// 3 -> 400,200,100,50
#define NL3D_QUADGRID_CLIP_CLUSTER_DEPTH 2
// ***************************************************************************
#define NL3D_QCC_LEFT_DOWN 0
#define NL3D_QCC_RIGHT_DOWN 1
#define NL3D_QCC_LEFT_UP 2
#define NL3D_QCC_RIGHT_UP 3
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CQuadGridClipClusterListDist::clipSons(uint minDistSetup)
{
for(uint i=minDistSetup; i<Models.size();i++)
{
CTransformShape ** pModel= Models[i].begin();
uint nSons= Models[i].size();
for(;nSons>0;nSons--, pModel++)
{
(*pModel)->traverseClip();
}
}
}
// ***************************************************************************
void CQuadGridClipClusterListDist::insertModel(uint distSetup, CTransformShape *model)
{
Models[distSetup].insert(model, &model->_QuadClusterListNode);
}
// ***************************************************************************
void CQuadGridClipClusterListDist::resetSons(CClipTrav *clipTrav)
{
for(uint i=0; i<Models.size();i++)
{
// clean up model list
CTransformShape ** pModel= Models[i].begin();
uint nSons= Models[i].size();
for(;nSons>0;nSons--, pModel++)
{
// link the model to the rootCluster
clipTrav->RootCluster->clipAddChild(*pModel);
}
// unlink all my sons from me
Models[i].clear();
}
}
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CQuadGridClipClusterQTreeNode::CQuadGridClipClusterQTreeNode()
{
Sons[0]=NULL;
Sons[1]=NULL;
Sons[2]=NULL;
Sons[3]=NULL;
Empty= true;
Owner= NULL;
RootNode= false;
LeafNode= false;
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::init(CQuadGridClipCluster *owner, uint level, bool rootNode, const NLMISC::CAABBox &pivot)
{
Owner= owner;
RootNode= rootNode;
PivotBBox= pivot;
// If not a leaf, create sons
if(level>0)
{
LeafNode= false;
// split pivot for sons
CAABBox pivotSon;
pivotSon.setSize(PivotBBox.getHalfSize());
float xMin= PivotBBox.getMin().x/2;
float yMin= PivotBBox.getMin().y/2;
float xMax= PivotBBox.getMax().x/2;
float yMax= PivotBBox.getMax().y/2;
float xCenter= PivotBBox.getCenter().x/2;
float yCenter= PivotBBox.getCenter().y/2;
// LeftDown
pivotSon.setCenter( CVector(xMin+xCenter,yMin+yCenter,0) );
Sons[NL3D_QCC_LEFT_DOWN]= new CQuadGridClipClusterQTreeNode;
Sons[NL3D_QCC_LEFT_DOWN]->init(owner, level-1, false, pivotSon);
// RightDown
pivotSon.setCenter( CVector(xMax+xCenter,yMin+yCenter,0) );
Sons[NL3D_QCC_RIGHT_DOWN]= new CQuadGridClipClusterQTreeNode;
Sons[NL3D_QCC_RIGHT_DOWN]->init(owner, level-1, false, pivotSon);
// LeftUp
pivotSon.setCenter( CVector(xMin+xCenter,yMax+yCenter,0) );
Sons[NL3D_QCC_LEFT_UP]= new CQuadGridClipClusterQTreeNode;
Sons[NL3D_QCC_LEFT_UP]->init(owner, level-1, false, pivotSon);
// RithgUp
pivotSon.setCenter( CVector(xMax+xCenter,yMax+yCenter,0) );
Sons[NL3D_QCC_RIGHT_UP]= new CQuadGridClipClusterQTreeNode;
Sons[NL3D_QCC_RIGHT_UP]->init(owner, level-1, false, pivotSon);
}
else
{
LeafNode= true;
}
// Create the distMax list only if root or leaf. No models in interleaved branches.
if( LeafNode)
ListNode.Models.resize(Owner->_NumDistTotal);
}
// ***************************************************************************
CQuadGridClipClusterQTreeNode::~CQuadGridClipClusterQTreeNode()
{
// erase sons
uint i;
for(i=0; i<4;i++)
{
if(Sons[i])
delete Sons[i];
Sons[i]= NULL;
}
// check my list
for(i=0; i<ListNode.Models.size();i++)
{
nlassert(ListNode.Models[i].empty());
}
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::clip(CClipTrav *clipTrav)
{
// if empty (test important for branch and leave clusters)
if(Empty)
return;
H_BEFORE( NL3D_QuadClip_NodeClip );
// Then clip against pyramid
bool unspecified= false;
bool visible= true;
for(sint i=0;i<(sint)clipTrav->WorldPyramid.size();i++)
{
// We are sure that pyramid has normalized plane normals.
if(!BBoxExt.clipBack(clipTrav->WorldPyramid[i]))
{
visible= false;
break;
}
// else test is the bbox is partially or fully in the plane
else if(!unspecified)
{
// if clipFront AND clipBack, it means partially.
if(BBoxExt.clipFront(clipTrav->WorldPyramid[i]))
unspecified= true;
}
}
H_AFTER( NL3D_QuadClip_NodeClip );
// if visible, parse sons
if(visible)
{
// clip sons or cluster sons
if(unspecified)
{
if( LeafNode)
{
// clip DistMax.
CVector c= BBoxExt.getCenter();
float dist= (c - clipTrav->CamPos).norm();
dist-= BBoxExt.getRadius();
sint minDistSetup= (sint)floor(Owner->_NumDist*dist/Owner->_DistMax);
// NB if too far, set _NumDist (ie will clip only the infinite objects ones)
clamp(minDistSetup, 0, (sint)Owner->_NumDist);
// clip the sons individually
H_AUTO( NL3D_QuadClip_SonsClip );
ListNode.clipSons(minDistSetup);
}
else
{
// clip cluster sons
Sons[0]->clip(clipTrav);
Sons[1]->clip(clipTrav);
Sons[2]->clip(clipTrav);
Sons[3]->clip(clipTrav);
}
}
else
{
// udpdate the sons, but don't clip, because we know they are fully visible.
clipTrav->ForceNoFrustumClip= true;
// show all cluster sons or sons
noFrustumClip(clipTrav);
// reset flag
clipTrav->ForceNoFrustumClip= false;
}
}
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::noFrustumClip(CClipTrav *clipTrav)
{
// if empty (test important for branch and leave clusters)
if(Empty)
return;
// clip the sons
if( LeafNode)
{
// clip DistMax.
CVector c= BBoxExt.getCenter();
float dist= (c - clipTrav->CamPos).norm();
dist-= BBoxExt.getRadius();
sint minDistSetup= (sint)floor(Owner->_NumDist*dist/Owner->_DistMax);
// NB if too far, set _NumDist (ie will clip only the infinite objects ones)
clamp(minDistSetup, 0, (sint)Owner->_NumDist);
// clip the sons
H_AUTO_USE( NL3D_QuadClip_SonsShowNoClip );
ListNode.clipSons(minDistSetup);
}
else
{
// forceShow of cluster sons
Sons[0]->noFrustumClip(clipTrav);
Sons[1]->noFrustumClip(clipTrav);
Sons[2]->noFrustumClip(clipTrav);
Sons[3]->noFrustumClip(clipTrav);
}
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::insertModel(const NLMISC::CAABBox &worldBBox, uint distSetup, CTransformShape *model)
{
// if leaf node, insert the model in the list
if( LeafNode )
{
if(Empty)
{
Empty= false;
BBox= worldBBox;
}
else
{
// extend the bbox with 2 corners of the incoming bbox (sufficient for an AABBox).
BBox.extend( worldBBox.getCenter() + worldBBox.getHalfSize() );
BBox.extend( worldBBox.getCenter() - worldBBox.getHalfSize() );
}
// insert in list
ListNode.insertModel(distSetup, model);
}
// else, recurs insert in branch
else
{
// choose what son according to pivot.
CQuadGridClipClusterQTreeNode *selectSon;
if( worldBBox.getCenter().y<PivotBBox.getCenter().y )
{
if( worldBBox.getCenter().x<PivotBBox.getCenter().x )
selectSon= Sons[NL3D_QCC_LEFT_DOWN];
else
selectSon= Sons[NL3D_QCC_RIGHT_DOWN];
}
else
{
if( worldBBox.getCenter().x<PivotBBox.getCenter().x )
selectSon= Sons[NL3D_QCC_LEFT_UP];
else
selectSon= Sons[NL3D_QCC_RIGHT_UP];
}
// insert in this cluster
selectSon->insertModel(worldBBox, distSetup, model);
// extend my boox according to this son cluster.
if(Empty)
{
Empty= false;
BBox= selectSon->BBox;
}
else
{
// extend the bbox with 2 corners of the son bbox (sufficient for an AABBox).
BBox.extend( selectSon->BBox.getCenter() + selectSon->BBox.getHalfSize() );
BBox.extend( selectSon->BBox.getCenter() - selectSon->BBox.getHalfSize() );
}
}
// update bboxExt
BBoxExt= BBox;
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::resetSons(CClipTrav *clipTrav)
{
ListNode.resetSons(clipTrav);
if(Sons[0])
{
Sons[0]->resetSons(clipTrav);
Sons[1]->resetSons(clipTrav);
Sons[2]->resetSons(clipTrav);
Sons[3]->resetSons(clipTrav);
}
}
// ***************************************************************************
void CQuadGridClipClusterQTreeNode::profileNumChildren(uint distLevel, uint &result) const
{
if(distLevel<ListNode.Models.size())
result+= ListNode.Models[distLevel].size();
// Add cluster sons
if(Sons[0])
{
Sons[0]->profileNumChildren(distLevel, result);
Sons[1]->profileNumChildren(distLevel, result);
Sons[2]->profileNumChildren(distLevel, result);
Sons[3]->profileNumChildren(distLevel, result);
}
}
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CQuadGridClipCluster::CQuadGridClipCluster(uint numDist, float distMax, const NLMISC::CAABBox &pivot)
{
_DistMax= distMax;
_NumDist= numDist;
_NumDistTotal= _NumDist+1;
_Root.init(this, NL3D_QUADGRID_CLIP_CLUSTER_DEPTH, true, pivot);
}
// ***************************************************************************
CQuadGridClipCluster::~CQuadGridClipCluster()
{
}
// ***************************************************************************
void CQuadGridClipCluster::addModel(const NLMISC::CAABBox &worldBBox, CTransformShape *model)
{
// check not already inserted
nlassert(!model->_QuadClusterListNode.isLinked());
// Add the model to the good distance list
float modelDistMax= model->getDistMax();
sint distSetup;
if(modelDistMax<0)
distSetup= _NumDist;
else
{
distSetup= (sint)floor(_NumDist*modelDistMax/_DistMax);
// NB: if >=_DistMax, then distSetup==_NumDist, meaning infinite distance (never dist clipped)
clamp(distSetup, 0, (sint)_NumDist);
}
// add / recurs to the quadtree
_Root.insertModel(worldBBox, distSetup, model);
}
// ***************************************************************************
void CQuadGridClipCluster::removeModel(CTransformShape *model)
{
model->_QuadClusterListNode.unlink();
}
// ***************************************************************************
void CQuadGridClipCluster::clip(CClipTrav *clipTrav)
{
H_AUTO_USE( NL3D_QuadClip_ClusterClip );
// clip the quadtree
_Root.clip(clipTrav);
}
// ***************************************************************************
void CQuadGridClipCluster::resetSons(CClipTrav *clipTrav)
{
_Root.resetSons(clipTrav);
}
// ***************************************************************************
sint CQuadGridClipCluster::profileNumChildren(uint distLevel) const
{
uint result= 0;
_Root.profileNumChildren(distLevel, result);
return result;
}
} // NL3D