1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/3d/quad_grid_clip_manager.h"
20 #include "nel/3d/scene.h"
21 #include "nel/3d/transform_shape.h"
22 #include "nel/3d/clip_trav.h"
23 #include "nel/misc/aabbox.h"
24 #include "nel/3d/cluster.h"
25 #include "nel/misc/hierarchical_timer.h"
29 using namespace NLMISC
;
39 // ***************************************************************************
40 void CQuadGridClipManager::registerBasic()
42 CScene::registerModel(QuadGridClipManagerId
, TransformId
, CQuadGridClipManager::creator
);
46 // ***************************************************************************
47 CQuadGridClipManager::CQuadGridClipManager()
53 // ***************************************************************************
54 CQuadGridClipManager::~CQuadGridClipManager()
60 // ***************************************************************************
61 void CQuadGridClipManager::init(float clusterSize
, uint numDist
, float maxDist
, float radiusMax
)
67 nlassert(clusterSize
>0);
68 _ClusterSize
= clusterSize
;
71 _RadiusMax
= radiusMax
;
74 // ***************************************************************************
75 void CQuadGridClipManager::reset()
77 // delete the clusters.
80 sint oldX0
, oldX1
, oldY0
, oldY1
;
87 for(sint y
=oldY0
; y
<oldY1
; y
++)
89 for(sint x
=oldX0
; x
<oldX1
; x
++)
91 deleteCaseModels(&getOwnerScene()->getClipTrav(), x
,y
);
96 _QuadGridClusterCases
.clear();
99 // reset others params.
105 // ***************************************************************************
106 void CQuadGridClipManager::updateClustersFromCamera(const CVector
&camPos
)
108 H_AUTO( NL3D_QuadClip_updateClusters
);
110 CClipTrav
*pClipTrav
= &getOwnerScene()->getClipTrav();
112 sint newX0
, newX1
, newY0
, newY1
;
113 sint oldX0
, oldX1
, oldY0
, oldY1
;
120 // compute the new square of clusters to build.
121 newX0
= (sint
)floor( (camPos
.x
- _RadiusMax
) / _ClusterSize
);
122 newX1
= (sint
)ceil( (camPos
.x
+ _RadiusMax
) / _ClusterSize
);
123 newY0
= (sint
)floor( (camPos
.y
- _RadiusMax
) / _ClusterSize
);
124 newY1
= (sint
)ceil( (camPos
.y
+ _RadiusMax
) / _ClusterSize
);
126 // keep an histeresis of one cluster: do not delete "young" clusters created before.
127 if(newX0
>= oldX0
+1) // NB: if newX0==oldX0+1, then newX0= _X => no change.
129 if(newY0
>= oldY0
+1) // same reasoning.
131 if(newX1
<= oldX1
-1) // NB: if newX1==oldX1-1, then newX1= oldX1 => no change.
133 if(newY1
<= oldY1
-1) // same reasoning.
136 // Have we got to update the array.
137 if(newX0
!=oldX0
|| newX1
!=oldX1
|| newY0
!=oldY0
|| newY1
!=oldY1
)
141 // delete olds models.
142 // simple: test all cases
143 for(y
=oldY0
; y
<oldY1
; y
++)
145 for(x
=oldX0
; x
<oldX1
; x
++)
147 // if out the new square?
148 if(x
<newX0
|| x
>=newX1
|| y
<newY0
|| y
>=newY1
)
149 deleteCaseModels(pClipTrav
, x
,y
);
154 // satic for alloc optimisation.
155 static vector
<CQuadGridClipCluster
*> newQuadGridClusterCases
;
156 sint newWidth
= newX1
-newX0
;
157 sint newHeight
= newY1
-newY0
;
158 newQuadGridClusterCases
.resize(newWidth
* newHeight
);
159 // simple: test all cases
160 for(y
=newY0
; y
<newY1
; y
++)
162 for(x
=newX0
; x
<newX1
; x
++)
164 CQuadGridClipCluster
*&newCase
= newQuadGridClusterCases
[ (y
-newY0
)*newWidth
+ (x
-newX0
) ];
166 // if out the old square?
167 if(x
<oldX0
|| x
>=oldX1
|| y
<oldY0
|| y
>=oldY1
)
169 // build the 2D bbox where the models should fit.
172 CVector(x
*_ClusterSize
,y
*_ClusterSize
,0),
173 CVector((x
+1)*_ClusterSize
,(y
+1)*_ClusterSize
,0) );
174 // build new case. Clusters are empty.
175 newCaseModels(newCase
, pivotBBox
);
180 CQuadGridClipCluster
*oldCase
= _QuadGridClusterCases
[ (y
-_Y
)*_Width
+ (x
-_X
) ];
187 // just copy from new.
188 _QuadGridClusterCases
= newQuadGridClusterCases
;
197 // ***************************************************************************
198 bool CQuadGridClipManager::linkModel(CTransformShape
*pTfmShp
)
200 H_AUTO( NL3D_QuadClip_linkModel
);
202 // use the position to get the cluster to use.
204 pTfmShp
->getAABBox (box
);
206 // Transform the box in the world
207 const CMatrix
&wm
= pTfmShp
->getWorldMatrix();
208 // compute center in world.
209 CVector cLocal
= box
.getCenter();
210 CVector cWorld
= wm
* cLocal
;
213 worldBBox
.setCenter(cWorld
);
214 CVector hs
= box
.getHalfSize();
217 for(uint i
=0;i
<8;i
++)
220 // compute the corner of the bbox.
222 if(i
&1) corner
.x
+=hs
.x
;
224 if((i
/2)&1) corner
.y
+=hs
.y
;
226 if((i
/4)&1) corner
.z
+=hs
.z
;
228 // Transform the corner in world.
229 corner
= wm
* corner
;
230 // Extend the bbox with it.
231 worldBBox
.extend(corner
);
235 // Position in the grid.
237 x
= (sint
)floor( cWorld
.x
/ _ClusterSize
);
238 y
= (sint
)floor( cWorld
.y
/ _ClusterSize
);
240 // verify if IN the current grid of clusters created.
241 if( x
>=_X
&& x
<_X
+_Width
&& y
>=_Y
&& y
<_Y
+_Height
)
243 // add the model and extend the bbox of this cluster.
244 CQuadGridClipCluster
*cluster
= _QuadGridClusterCases
[ (y
-_Y
)*_Width
+ (x
-_X
) ];
246 // if this cluster is empty, add it now to the list of not empty (do the test before add)
247 if( cluster
->isEmpty() )
249 _NotEmptyQuadGridClipClusters
.insert(cluster
, &cluster
->ListNode
);
252 // add the model => no more empty
253 cluster
->addModel(worldBBox
, pTfmShp
);
265 // ***************************************************************************
266 void CQuadGridClipManager::deleteCaseModels(CClipTrav
*pClipTrav
, sint x
, sint y
)
268 H_AUTO( NL3D_QuadClip_deleteCaseModels
);
270 nlassert(x
>=_X
&& x
<_X
+_Width
&& y
>=_Y
&& y
<_Y
+_Height
);
272 CQuadGridClipCluster
*cluster
= _QuadGridClusterCases
[ (y
-_Y
)*_Width
+ (x
-_X
) ];
274 // first, unlink all sons from cluster, and link thems to RootCluster.
275 cluster
->resetSons(pClipTrav
);
277 // delete the cluster. NB: auto-unlinked from _NotEmptyQuadGridClipClusters
280 // NB: do not delete array for alloc/free optimisation.
284 // ***************************************************************************
285 void CQuadGridClipManager::newCaseModels(CQuadGridClipCluster
*&cluster
, const NLMISC::CAABBox
&pivotBbox
)
287 H_AUTO( NL3D_QuadClip_newCaseModels
);
290 cluster
= new CQuadGridClipCluster(_NumDist
, _MaxDist
, pivotBbox
);
294 // ***************************************************************************
295 void CQuadGridClipManager::traverseClip()
297 CClipTrav
*pClipTrav
= &getOwnerScene()->getClipTrav();
299 // Run All NotEmpty Clusters,
300 CQuadGridClipCluster
**it
;
301 it
= _NotEmptyQuadGridClipClusters
.begin();
302 uint numClusters
= _NotEmptyQuadGridClipClusters
.size();
303 for(;numClusters
>0;numClusters
--,it
++)
305 (*it
)->clip(pClipTrav
);
310 // ***************************************************************************
311 void CQuadGridClipManager::profile() const
313 nlinfo(" ***** CQuadGridClipManager stats");
314 nlinfo(" There is %d clusters", _Width
*_Height
);
315 sint numEmptyClusters
= 0;
316 float minAreaRatio
= 1000;
317 float maxAreaRatio
= 0;
318 float meanAreaRatio
= 0;
321 for(sint y
=0;y
<_Height
;y
++)
323 for(sint x
=0;x
<_Width
;x
++)
325 const CQuadGridClipCluster
*cluster
= _QuadGridClusterCases
[y
*_Width
+x
];
326 if( cluster
->isEmpty() )
330 CAABBox bb
= cluster
->getBBox();
331 float area
= (bb
.getSize().x
*bb
.getSize().y
) / (_ClusterSize
*_ClusterSize
);
332 meanAreaRatio
+= area
;
333 minAreaRatio
= min( minAreaRatio
, area
);
334 maxAreaRatio
= max( maxAreaRatio
, area
);
340 if( numEmptyClusters
==_Width
*_Height
)
342 nlinfo( " The ClipManager is completely Empty!!!!");
346 sint numClusters
= _Width
*_Height
-numEmptyClusters
;
347 nlinfo( " This Level has %d clusters not empty over %d", numClusters
, _Width
*_Height
);
348 meanAreaRatio
/= numClusters
;
349 nlinfo(" . minAreaRatio= %f", minAreaRatio
);
350 nlinfo(" . maxAreaRatio= %f", maxAreaRatio
);
351 nlinfo(" . meanAreaRatio= %f", meanAreaRatio
);
353 // Do Stats per distance setup
354 for(uint lvl
=0;lvl
<_NumDist
+1;lvl
++)
356 nlinfo(" * Stats for Distance up to %f m", lvl
<_NumDist
?(lvl
+1)*_MaxDist
/_NumDist
:-1.0f
);
357 sint minNumChildren
= 100000000;
358 sint maxNumChildren
= 0;
359 float meanNumChildren
= 0;
360 sint totalNumChildren
= 0;
363 for(sint y
=0;y
<_Height
;y
++)
365 for(sint x
=0;x
<_Width
;x
++)
367 const CQuadGridClipCluster
*cluster
= _QuadGridClusterCases
[y
*_Width
+x
];
368 if( cluster
->isEmpty() )
372 // count number of sons
374 numSons
= cluster
->profileNumChildren(lvl
);
375 meanNumChildren
+= numSons
;
376 totalNumChildren
+= numSons
;
377 minNumChildren
= min( minNumChildren
, numSons
);
378 maxNumChildren
= max( maxNumChildren
, numSons
);
384 meanNumChildren
/= numClusters
;
385 nlinfo(" . minNumChildren= %d", minNumChildren
);
386 nlinfo(" . maxNumChildren= %d", maxNumChildren
);
387 nlinfo(" . meanNumChildren= %f", meanNumChildren
);
388 nlinfo(" . totalNumChildren= %d", totalNumChildren
);