// 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/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; i0;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; i0;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; iWorldPyramid.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().yinsertModel(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(distLevelprofileNumChildren(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