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/u_visual_collision_entity.h"
21 #include "nel/3d/visual_collision_entity.h"
22 #include "nel/3d/landscape.h"
23 #include "nel/3d/dru.h"
24 #include "nel/3d/driver.h"
25 #include "nel/3d/tile_bank.h"
26 #include "nel/misc/hierarchical_timer.h"
30 using namespace NLMISC
;
40 // ***************************************************************************
41 // should be at least 2 meters.
42 const float CVisualCollisionEntity::BBoxRadius
= 10;
43 const float CVisualCollisionEntity::BBoxRadiusZ
= 20;
44 const uint32
CVisualCollisionEntity::_StartPatchQuadBlockSize
= 64; // 256 octets per entity minimum.
45 vector
<CPatchBlockIdent
> CVisualCollisionEntity::_TmpBlockIds
;
46 vector
<CPatchQuadBlock
*> CVisualCollisionEntity::_TmpPatchQuadBlocks
;
49 // ***************************************************************************
50 CVisualCollisionEntity::CVisualCollisionEntity(CVisualCollisionManager
*owner
) : _LandscapeQuadGrid(owner
)
53 _PatchQuadBlocks
.reserve(_StartPatchQuadBlockSize
);
55 _CurrentBBoxValidity
.setHalfSize(CVector::Null
);
59 _SnapToRenderedTesselation
= true;
65 // ***************************************************************************
66 CVisualCollisionEntity::~CVisualCollisionEntity()
68 // delete the _PatchQuadBlocks.
69 for(sint i
=0; i
<(sint
)_PatchQuadBlocks
.size(); i
++)
71 _Owner
->deletePatchQuadBlock(_PatchQuadBlocks
[i
]);
73 _PatchQuadBlocks
.clear();
75 // delete the quadgrid.
76 _LandscapeQuadGrid
.clear();
80 // ***************************************************************************
81 bool CVisualCollisionEntity::snapToGround(CVector
&pos
)
84 // Not optimized, but doesn't matter (one cross product is negligible).
85 return snapToGround(pos
, normal
);
89 // ***************************************************************************
90 CTrianglePatch
*CVisualCollisionEntity::getPatchTriangleUnderUs(const CVector
&pos
, CVector
&res
)
92 // verify if landscape (refptr) is here.
93 if(_Owner
->_Landscape
==NULL
)
102 // If last call was valid, and if same pos (input or output), return cached information
103 if(_LastGPTValid
&& (pos
==_LastGPTPosInput
|| pos
==_LastGPTPosOutput
) )
106 res
= _LastGPTPosOutput
;
107 /* don't modify _LastGPTPosInput cache, for best cache behavior in all cases.
108 1/ this is not necessary (here, if pos!=_LastGPTPosInput, THEN pos==_LastGPTPosOutput, no other possibilities)
109 2/ it causes problems when getPatchTriangleUnderUs() is called randomly with the unsnapped or snapped position:
110 1st Time: zin:9.0 => zout:9.5 cache fail (ok, 1st time...)
111 2nd Time: zin:9.0 => zout:9.5 cache OK (_LastGPTPosInput.z==zin)
112 3rd Time: zin:9.5 => zout:9.5 cache OK (_LastGPTPosOutput.z==zin)
113 4th Time: zin:9.0 => zout:9.5 cache FAILS (_LastGPTPosInput.z= 9.5 => !=zin)
115 // and return ptr on cache.
116 return &_LastGPTTrianglePatch
;
120 // update the cache of tile info near this position.
122 testComputeLandscape(pos
);
125 // find possible faces under the entity.
127 CVisualTileDescNode
*ptr
= _LandscapeQuadGrid
.select(pos
);
130 // find the better face under the entity.
132 float sqrBestDist
= sqr(1000.f
);
134 // build the vertical ray.
135 CVector segP0
= pos
- CVector(0,0,100);
136 CVector segP1
= pos
+ CVector(0,0,100);
139 // triangles builded from this list.
140 static vector
<CTrianglePatch
> testTriangles
;
141 // NB: not so many reallocation here, because static.
142 testTriangles
.clear();
143 sint bestTriangle
= 0;
146 // For all the faces in this quadgrid node.
149 // what is the quad block of this tile Id.
150 sint qbId
= ptr
->PatchQuadBlocId
;
151 nlassert(qbId
>=0 && qbId
<(sint
)_PatchQuadBlocks
.size());
152 CPatchQuadBlock
&qb
= *_PatchQuadBlocks
[qbId
];
154 // Build the 2 triangles of this tile Id.
155 sint idStart
= (sint
)testTriangles
.size();
156 testTriangles
.resize(idStart
+2);
157 qb
.buildTileTriangles((uint8
)ptr
->QuadId
, &testTriangles
[idStart
]);
159 // Test the 2 triangles.
160 for(sint i
=0; i
<2; i
++)
162 CTrianglePatch
&tri
= testTriangles
[idStart
+i
];
163 // test if the ray intersect.
164 // NB: triangleIntersect() is faster than CTriangle::intersect().
165 if(triangleIntersect(tri
, segP0
, segP1
, hit
))
167 // find the nearest triangle.
168 float sqrdist
= (hit
-pos
).sqrnorm();
169 if(sqrdist
<sqrBestDist
)
171 bestTriangle
= idStart
+i
;
173 sqrBestDist
= sqrdist
;
185 if(sqrBestDist
<sqr(1000))
189 _LastGPTTrianglePatch
= testTriangles
[bestTriangle
];
190 _LastGPTPosInput
= pos
;
191 _LastGPTPosOutput
= res
;
192 // and return ptr on cache.
193 return &_LastGPTTrianglePatch
;
197 _LastGPTValid
= false;
204 // ***************************************************************************
205 bool CVisualCollisionEntity::snapToGround(CVector
&pos
, CVector
&normal
)
207 // Get Patch Triangle Under Us
209 CTrianglePatch
*tri
= getPatchTriangleUnderUs(pos
, res
);
211 // result. NB: if not found, dot not modify.
214 if(_SnapToRenderedTesselation
)
216 // snap the position to the nearest tesselation.
219 // snap the position to the current rendered tesselation.
220 snapToLandscapeCurrentTesselation(pos
, *tri
);
224 // just snap to the accurate tile tesselation.
229 // compute the normal.
230 normal
= (tri
->V1
-tri
->V0
)^(tri
->V2
-tri
->V0
);
240 // ***************************************************************************
241 void CVisualCollisionEntity::computeUvForPos(const CTrianglePatch
&tri
, const CVector
&pos
, CUV
&uv
)
243 // compute UV gradients.
246 tri
.computeGradient(tri
.Uv0
.U
, tri
.Uv1
.U
, tri
.Uv2
.U
, Gu
);
247 tri
.computeGradient(tri
.Uv0
.V
, tri
.Uv1
.V
, tri
.Uv2
.V
, Gv
);
249 uv
.U
= tri
.Uv0
.U
+ Gu
*(pos
-tri
.V0
);
250 uv
.V
= tri
.Uv0
.V
+ Gv
*(pos
-tri
.V0
);
254 // ***************************************************************************
255 void CVisualCollisionEntity::snapToLandscapeCurrentTesselation(CVector
&pos
, const CTrianglePatch
&tri
)
257 // compute UV for position.
259 computeUvForPos(tri
, pos
, uv
);
261 // Ask pos to landscape.
263 posLand
= _Owner
->_Landscape
->getTesselatedPos(tri
.PatchId
, uv
);
270 // ***************************************************************************
271 // TestYoyo. For Precision problem.
272 /*static bool testLine(CVector &p1, CVector &p0, const CVector &pos0)
276 float a,b,c; // 2D cartesian coefficients of line in plane X/Y.
281 norm= sqrtf(sqr(a) + sqr(b));
284 c= -(p0.x*a + p0.y*b);
285 if( (a*pos0.x + b*pos0.y + c) < -epsilon)
292 // ***************************************************************************
293 bool CVisualCollisionEntity::triangleIntersect2DGround(CTriangle
&tri
, const CVector
&pos0
)
299 // TestYoyo. Test for precision problems.
300 /*if( testLine(p1, p0, pos0) && testLine(p2, p1, pos0) && testLine(p0, p2, pos0) )
302 nlinfo("Found Tri For Pos: %.07f, %.07f\n P0: %.07f, %.07f\n P1: %.07f, %.07f\n P2: %.07f, %.07f",
303 pos0.x, pos0.y, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y);
306 // Test if the face enclose the pos in X/Y plane.
307 // NB: compute and using a BBox to do a rapid test is not a very good idea, since it will
308 // add an overhead which is NOT negligeable compared to the following test.
309 float a
,b
,c
; // 2D cartesian coefficients of line in plane X/Y.
313 c
= -(p0
.x
*a
+ p0
.y
*b
);
314 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
318 c
= -(p1
.x
*a
+ p1
.y
*b
);
319 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
323 c
= -(p2
.x
*a
+ p2
.y
*b
);
324 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
329 // ***************************************************************************
330 bool CVisualCollisionEntity::triangleIntersect2DCeil(CTriangle
&tri
, const CVector
&pos0
)
336 // Test if the face enclose the pos in X/Y plane.
337 // NB: compute and using a BBox to do a rapid test is not a very good idea, since it will
338 // add an overhead which is NOT negligeable compared to the following test.
339 float a
,b
,c
; // 2D cartesian coefficients of line in plane X/Y.
343 c
= -(p0
.x
*a
+ p0
.y
*b
);
344 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) > 0) return false;
348 c
= -(p1
.x
*a
+ p1
.y
*b
);
349 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) > 0) return false;
353 c
= -(p2
.x
*a
+ p2
.y
*b
);
354 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) > 0) return false;
359 // ***************************************************************************
360 bool CVisualCollisionEntity::triangleIntersect(CTriangle
&tri
, const CVector
&pos0
, const CVector
&pos1
, CVector
&hit
)
367 if( _GroundMode
&& triangleIntersect2DGround(tri
, pos0
) )
369 if(!ok
&& _CeilMode
&& triangleIntersect2DCeil(tri
, pos0
) )
375 // Compute the possible height.
379 plane
.make (p0
, p1
, p2
);
380 // intersect the vertical line with the plane.
381 tmp
= plane
.intersect(pos0
, pos1
);
384 // Test if it would fit in the wanted field.
385 if(h
>pos1
.z
) return false;
386 if(h
<pos0
.z
) return false;
389 // For cache completness, ensure that X and Y don't move, take same XY than pos0
396 // ***************************************************************************
397 void CVisualCollisionEntity::testComputeLandscape(const CVector
&pos
)
399 // if new position is out of the bbox surounding the entity.
400 if(_CurrentBBoxValidity
.getHalfSize()==CVector::Null
|| !_CurrentBBoxValidity
.include(pos
))
402 // must recompute the data around the entity.
403 doComputeLandscape(pos
);
407 // ***************************************************************************
408 void CVisualCollisionEntity::doComputeLandscape(const CVector
&pos
)
414 // compute the bbox which must includes the patchQuadBlocks
415 CAABBox bboxToIncludePatchs
;
416 bboxToIncludePatchs
.setCenter(pos
);
417 bboxToIncludePatchs
.setHalfSize(CVector(BBoxRadius
, BBoxRadius
, BBoxRadiusZ
));
418 // setup the _CurrentBBoxValidity with same values, but BBoxRadiusZ/2
419 _CurrentBBoxValidity
.setCenter(pos
);
420 _CurrentBBoxValidity
.setHalfSize(CVector(BBoxRadius
, BBoxRadius
, BBoxRadiusZ
/2));
423 // Search landscape blocks which are in the bbox.
425 _Owner
->_Landscape
->buildPatchBlocksInBBox(bboxToIncludePatchs
, _TmpBlockIds
);
429 // Recompute PatchQuadBlockcs.
431 // This parts try to keeps old patch blocks so they are not recomputed if they already here.
433 // sort PatchBlockIdent.
434 sort(_TmpBlockIds
.begin(), _TmpBlockIds
.end());
436 // Copy old array of ptr (ptr copy only).
437 _TmpPatchQuadBlocks
= _PatchQuadBlocks
;
439 // allocate dest array.
440 _PatchQuadBlocks
.resize(_TmpBlockIds
.size());
442 // Traverse all current patchBlocks, deleting old ones no longer needed, and creating new ones.
443 // this algorithm suppose both array are sorted.
445 // parse until dest is filled.
446 for(i
=0; i
<(sint
)_PatchQuadBlocks
.size();)
448 // get requested new BlockIdent.
449 CPatchBlockIdent newBi
= _TmpBlockIds
[i
];
451 // get requested old BlockIdent in the array.
453 CPatchBlockIdent oldBi
;
454 if(iOld
==_TmpPatchQuadBlocks
.size())
457 oldBi
= _TmpPatchQuadBlocks
[iOld
]->PatchBlockId
;
459 // if no more old blocks, or if new Block is < than current, we must create a new block, and insert it.
460 if(oldEnd
|| newBi
< oldBi
)
462 // allocate the patch block.
463 _PatchQuadBlocks
[i
]= _Owner
->newPatchQuadBlock();
464 // fill the patch block.
465 _PatchQuadBlocks
[i
]->PatchBlockId
= _TmpBlockIds
[i
];
466 _Owner
->_Landscape
->fillPatchQuadBlock(*_PatchQuadBlocks
[i
]);
468 // next new patch block.
471 // else if current new Block is same than old block, just copy ptr.
472 else if(newBi
==oldBi
)
474 // just copy ptr with the old one.
475 _PatchQuadBlocks
[i
]= _TmpPatchQuadBlocks
[iOld
];
477 // next new and old patch block.
481 // else, this old block is no longer used, delete it.
484 _Owner
->deletePatchQuadBlock(_TmpPatchQuadBlocks
[iOld
]);
485 // next old patch block.
489 // Here, must delete old blocks not yet processed.
490 for(;iOld
<_TmpPatchQuadBlocks
.size(); iOld
++)
492 _Owner
->deletePatchQuadBlock(_TmpPatchQuadBlocks
[iOld
]);
494 _TmpPatchQuadBlocks
.clear();
497 // Compute the quadGrid.
500 // Compute a delta so elt position for CLandscapeCollisionGrid are positive, and so fastFloor() used will work.
502 // center the position on 0.
503 // floor may be important for precision when the delta is applied.
504 delta
.x
= (float)floor(-pos
.x
);
505 delta
.y
= (float)floor(-pos
.y
);
507 // We must always have positive values for patchBlocks vertices.
508 float val
= (float)ceil(BBoxRadius
+ 256);
509 // NB: 256 is a security. Because of size of patchs, a value of 32 at max should be sufficient (64 for bigger patch (gfx))
510 // we are large because doesn't matter, the CLandscapeCollisionGrid tiles.
514 // rebuild the quadGrid.
515 _LandscapeQuadGrid
.build(_PatchQuadBlocks
, delta
);
520 // ***************************************************************************
521 bool CVisualCollisionEntity::getStaticLightSetup(NLMISC::CRGBA sunAmbient
, const CVector
&pos
,
522 std::vector
<CPointLightInfluence
> &pointLightList
, uint8
&sunContribution
, NLMISC::CRGBA
&localAmbient
)
524 // Get Patch Triangle Under Us
526 CTrianglePatch
*tri
= getPatchTriangleUnderUs(pos
, res
);
528 // For now, no special ambient support on landscape => take sunAmbient
529 localAmbient
= sunAmbient
;
531 // result. NB: if not found, dot not modify.
534 // compute UV for position.
536 computeUvForPos(*tri
, pos
, uv
);
538 // get the sunContribution
539 sunContribution
= _Owner
->_Landscape
->getLumel(tri
->PatchId
, uv
);
540 // see getStaticLightSetup.
541 sunContribution
= _Owner
->_SunContributionLUT
[sunContribution
];
543 // append any lights of interest.
544 _Owner
->_Landscape
->appendTileLightInfluences(tri
->PatchId
, uv
, pointLightList
);
550 // Suppose full Sun Contribution, and don't add any pointLight
551 sunContribution
= 255;
558 // ***************************************************************************
559 bool CVisualCollisionEntity::getSurfaceInfo(const CVector
&pos
, CSurfaceInfo
&surfaceInfo
)
561 H_AUTO( NL3D_CVisualCollisionEntity_getSurfaceInfo
)
562 // Get Patch Triangle Under Us
564 CTrianglePatch
*tri
= getPatchTriangleUnderUs(pos
, res
);
566 // result. NB: if not found, dot not modify.
569 // compute UV for position.
571 computeUvForPos(*tri
, pos
, uv
);
573 // get the tileelement
574 CTileElement
*tileElm
= _Owner
->_Landscape
->getTileElement(tri
->PatchId
, uv
);
578 uint16 tileId
= tileElm
->Tile
[0];
579 if (tileId
!= NL_TILE_ELM_LAYER_EMPTY
)
582 CTileBank
&tileBank
= _Owner
->_Landscape
->TileBank
;
584 // Get xref info for this tile
587 CTileBank::TTileType type
;
588 tileBank
.getTileXRef ((int)tileId
, tileSet
, number
, type
);
590 // Get the tileset from layer 0
591 const CTileSet
* tileSetPtr
= tileBank
.getTileSet (tileSet
);
594 surfaceInfo
.UserSurfaceData
= tileSetPtr
->SurfaceData
;
605 // ***************************************************************************
606 void CVisualCollisionEntity::displayDebugGrid(IDriver
&drv
) const
608 // static to not reallocate each frame
609 static CMaterial mat
;
610 static bool inited
= false;
616 static vector
<CLine
> lineList
;
619 // Build lines for all patch quad blocks.
620 for(uint i
=0;i
<_PatchQuadBlocks
.size();i
++)
622 CPatchQuadBlock
&pqb
= *_PatchQuadBlocks
[i
];
623 // Parse all quads of this patch block.
624 CPatchBlockIdent
&pbid
= pqb
.PatchBlockId
;
625 for(uint t
= pbid
.T0
; t
<pbid
.T1
; t
++)
627 for(uint s
= pbid
.S0
; s
<pbid
.S1
; s
++)
629 // compute quad coordinate in pqb.
630 uint sd0
= (s
-pbid
.S0
);
631 uint td0
= (t
-pbid
.T0
);
634 // get 4 vertex coord of quad
635 const CVector
&p0
= pqb
.Vertices
[sd0
+ td0
*NL_PATCH_BLOCK_MAX_VERTEX
];
636 const CVector
&p1
= pqb
.Vertices
[sd0
+ td1
*NL_PATCH_BLOCK_MAX_VERTEX
];
637 const CVector
&p2
= pqb
.Vertices
[sd1
+ td1
*NL_PATCH_BLOCK_MAX_VERTEX
];
638 const CVector
&p3
= pqb
.Vertices
[sd1
+ td0
*NL_PATCH_BLOCK_MAX_VERTEX
];
641 lineList
.push_back(CLine(p0
, p1
));
642 lineList
.push_back(CLine(p1
, p2
));
643 lineList
.push_back(CLine(p2
, p3
));
644 lineList
.push_back(CLine(p3
, p0
));
645 lineList
.push_back(CLine(p0
, p2
));
650 // Display the lines.
651 CDRU::drawLinesUnlit(lineList
, mat
, drv
);