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/mini_col.h"
20 #include "nel/misc/aabbox.h"
21 #include "nel/3d/quad_grid.h"
23 using namespace NLMISC
;
33 static const sint QuadDepth
= 10;
36 // Element for grid lookup.
37 static const sint GridSize
=512;
38 static const float GridEltSize
=2;
41 // ***************************************************************************
46 _Grid
.create(GridSize
, GridEltSize
);
50 // ***************************************************************************
51 void CMiniCol::addFaces(const std::vector
<CTriangle
> &faces
, uint16 zoneId
, uint16 patchId
)
53 for(sint i
=0;i
<(sint
)faces
.size();i
++)
55 const CTriangle
&f
= faces
[i
];
62 node
.Plane
.make(f
.V0
, f
.V1
, f
.V2
);
65 _Grid
.insert(box
.getMin(), box
.getMax(), node
);
70 // ***************************************************************************
71 void CMiniCol::addLandscapePart(uint16 zoneId
, uint16 patchId
)
73 vector
<CTriangle
> faces
;
74 _Landscape
->buildCollideFaces(zoneId
, patchId
, faces
);
75 addFaces(faces
, zoneId
, patchId
);
79 // ***************************************************************************
80 void CMiniCol::removeLandScapePart(uint16 zoneId
, uint16 patchId
, const CBSphere
&sphere
)
82 // Build the AAbox which englobe the bsphere of the patch.
84 bb
.setCenter(sphere
.Center
);
85 float l
= sphere
.Radius
;
86 bb
.setHalfSize(CVector(l
,l
,l
));
88 // For optimisation, select only faces which are IN the bbox of the patch.
89 _Grid
.select(bb
.getMin(), bb
.getMax());
90 CQuadGrid
<CFace
>::CIterator iFace
;
91 for(iFace
= _Grid
.begin();iFace
!=_Grid
.end();)
93 if((*iFace
).isFromPatch(zoneId
, patchId
))
94 iFace
= _Grid
.erase(iFace
);
101 // ***************************************************************************
102 void CMiniCol::init(CLandscape
*land
, float radMin
, float radDelta
)
106 _RadMax
= radMin
+radDelta
;
110 // ***************************************************************************
111 void CMiniCol::addZone(uint16 zoneId
)
115 // landscape must have been inited.
116 nlassert(_Landscape
);
117 const CZone
*zone
= _Landscape
->getZone(zoneId
);
118 // zone must be loaded into landscape.
122 newZone
.ZoneId
= zoneId
;
123 newZone
.Sphere
.Center
= zone
->getZoneBB().getCenter();
124 newZone
.Sphere
.Radius
= zone
->getZoneBB().getRadius();
125 newZone
.Patchs
.resize(zone
->getNumPatchs());
126 for(sint i
=0;i
<zone
->getNumPatchs();i
++)
128 newZone
.Patchs
[i
].Sphere
= zone
->getPatchBSphere(i
);
131 // Add it to the set (if not already done...).
132 _Zones
.insert(newZone
);
136 // ***************************************************************************
137 void CMiniCol::removeZone(uint16 zoneId
)
142 // First, delete all patch from the grid.
143 //=======================================
144 // Fill the key part only.
145 delZone
.ZoneId
= zoneId
;
146 // Find the zone (or quit).
147 TZoneSet::iterator itZone
;
148 itZone
= _Zones
.find(delZone
);
149 if(itZone
==_Zones
.end())
152 CZoneIdent
&zone
= const_cast<CZoneIdent
&>(*itZone
);
153 for(sint i
=0;i
<(sint
)zone
.Patchs
.size();i
++)
155 CPatchIdent
&pa
= zone
.Patchs
[i
];
159 removeLandScapePart(uint16(zone
.ZoneId
), uint16(i
), pa
.Sphere
);
161 zone
.NPatchInserted
--;
167 _Zones
.erase(delZone
);
172 // ***************************************************************************
173 void CMiniCol::setCenter(const CVector
& center
)
175 CBSphere
BMin(center
, _RadMin
), BMax(center
, _RadMax
);
177 // For all zones, test if must insert patchs..
178 TZoneSet::iterator itZone
;
179 for(itZone
= _Zones
.begin();itZone
!=_Zones
.end();itZone
++)
181 CZoneIdent
&zone
= const_cast<CZoneIdent
&>(*itZone
);
183 // Tests must be done in 2D...
184 BMin
.Center
.z
= zone
.Sphere
.Center
.z
;
185 BMax
.Center
.z
= zone
.Sphere
.Center
.z
;
187 // Must test first if the zone is IN the area.
188 //=============================================
190 if(zone
.NPatchInserted
==0)
192 if(BMin
.intersect(zone
.Sphere
))
198 // Then for all patchs, must test if the patch must be inserted, or rejected.
199 //=============================================
202 for(sint i
=0;i
<(sint
)zone
.Patchs
.size();i
++)
204 CPatchIdent
&pa
= zone
.Patchs
[i
];
206 // Tests must be done in 2D...
207 BMin
.Center
.z
= pa
.Sphere
.Center
.z
;
208 BMax
.Center
.z
= pa
.Sphere
.Center
.z
;
212 // Reject the patch, if entirely OUT the max radius.
213 if(!BMax
.intersect(pa
.Sphere
))
215 removeLandScapePart(uint16(zone
.ZoneId
), uint16(i
), pa
.Sphere
);
217 zone
.NPatchInserted
--;
222 // Insert the pacth, if only partially IN the min radius.
223 if(BMin
.intersect(pa
.Sphere
))
225 addLandscapePart(uint16(zone
.ZoneId
), uint16(i
));
227 zone
.NPatchInserted
++;
236 // ***************************************************************************
237 bool CMiniCol::snapToGround(CVector
&pos
, float hup
, float hbot
)
244 // Select quad nodes which contains pos.
251 // For each face, test if it is under pos, then test if height is correct.
252 CQuadGrid
<CFace
>::CIterator iFace
;
253 for(iFace
= _Grid
.begin();iFace
!=_Grid
.end();iFace
++)
255 CTriangle
&pFace
= (*iFace
).Face
;
256 CPlane
&pPlane
= (*iFace
).Plane
;
257 // Order is important.
258 CVector
&p0
= pFace
.V0
;
259 CVector
&p1
= pFace
.V1
;
260 CVector
&p2
= pFace
.V2
;
262 // TOIMP: This is VERY SLOW!!! (hope that the quadtree will help, but it still very slow...).
264 // Yoyo Debug, test, if the point may be IN the bbox.
266 bbFace.setCenter(p0);
270 bext.z= maxof(p0.z, p1.z, p2.z)+hbot;
272 bext.z= minof(p0.z, p1.z, p2.z)-hup;
274 if(!bbFace.include(pos))
277 // Test if the face enclose the pos in X/Y plane.
278 // NB: compute and using a BBox to do a rapid test is not a very good idea, since it will
279 // add an overhead which is NOT negligeable compared to the following test.
280 float a
,b
,c
; // 2D cartesian coefficients of line in plane X/Y.
284 c
= -(p0
.x
*a
+ p0
.y
*b
);
285 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
289 c
= -(p1
.x
*a
+ p1
.y
*b
);
290 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
294 c
= -(p2
.x
*a
+ p2
.y
*b
);
295 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
298 // Compute the possible height.
300 // intersect the vertical line with the plane.
301 tmp
= pPlane
.intersect(pos
, pos
-CVector(0,0,100));
304 // CTriangle intersect() method.
306 if(pFace.intersect(b1, b2, tmp, pPlane))
310 // Test if it would fit in the wanted field.
311 if(h
>pos
.z
+hup
) continue;
312 if(h
<pos
.z
-hbot
) continue;
322 height
= max(height
,h
);
335 // ***************************************************************************
336 bool CMiniCol::getGroundNormal(const CVector
&pos
, CVector
&normal
, float hup
, float hbot
)
343 // Select quad nodes which contains pos.
350 // For each face, test if it is under pos, then test if height is correct.
351 CQuadGrid
<CFace
>::CIterator iFace
;
352 for(iFace
= _Grid
.begin();iFace
!=_Grid
.end();iFace
++)
354 CTriangle
&pFace
= (*iFace
).Face
;
355 CPlane
&pPlane
= (*iFace
).Plane
;
356 // Order is important.
357 CVector
&p0
= pFace
.V0
;
358 CVector
&p1
= pFace
.V1
;
359 CVector
&p2
= pFace
.V2
;
361 // TOIMP: This is VERY SLOW!!! (hope that the quadtree will help, but it still very slow...).
363 // Yoyo Debug, test, if the point may be IN the bbox.
365 bbFace
.setCenter(p0
);
369 bext
.z
= maxof(p0
.z
, p1
.z
, p2
.z
)+hbot
;
371 bext
.z
= minof(p0
.z
, p1
.z
, p2
.z
)-hup
;
373 if(!bbFace
.include(pos
))
376 // Test if the face enclose the pos in X/Y plane.
377 // NB: compute and using a BBox to do a rapid test is not a very good idea, since it will
378 // add an overhead which is NOT negligeable compared to the following test.
379 float a
,b
,c
; // 2D cartesian coefficients of line in plane X/Y.
383 c
= -(p0
.x
*a
+ p0
.y
*b
);
384 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
388 c
= -(p1
.x
*a
+ p1
.y
*b
);
389 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
393 c
= -(p2
.x
*a
+ p2
.y
*b
);
394 if( (a
*pos
.x
+ b
*pos
.y
+ c
) < 0) continue;
397 // Compute the possible height.
399 // intersect the vertical line with the plane.
400 tmp
= pPlane
.intersect(pos
, pos
-CVector(0,0,100));
402 // Test if it would fit in the wanted field.
403 if(h
>pos
.z
+hup
) continue;
404 if(h
<pos
.z
-hbot
) continue;
411 normal
= pPlane
.getNormal();
417 normal
= pPlane
.getNormal();
427 // ***************************************************************************
428 bool CMiniCol::testMove(const CVector
&prec
, CVector
&cur
)
430 CVector dir
= cur
-prec
;
434 float anglemax
= 65; // 65 degrees.
435 anglemax
= (float)tan( anglemax
*Pi
/180);
437 // Must not go to near of a wall.
438 CVector test
= cur
+dir
*0.5;
439 float norm
= (test
-prec
).norm();
441 if(!snapToGround(test
, norm
, norm
))
448 // Must test and snap the current position.
449 norm
= (cur
-prec
).norm();
451 if(!snapToGround(cur
, norm
, norm
))
461 // ***************************************************************************
462 void CMiniCol::getFaces(std::vector
<CTriangle
> &triresult
, const CAABBox
&bbox
)
467 _Grid
.select(bbox
.getMin(),bbox
.getMax());
469 // For each face, test if it is under pos, then test if height is correct.
470 CQuadGrid
<CFace
>::CIterator iFace
;
471 for(iFace
= _Grid
.begin();iFace
!=_Grid
.end();iFace
++)
473 CTriangle
&face
= (*iFace
).Face
;
475 if(bbox
.intersect(face
.V0
, face
.V1
, face
.V2
))
477 triresult
.push_back(face
);