Show bonus/malus timer text if available
[ryzomcore.git] / nel / src / 3d / visual_collision_entity.cpp
blob8f3b97e685eb290242075dfb1482facb610e301c
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
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"
29 using namespace std;
30 using namespace NLMISC;
32 #ifdef DEBUG_NEW
33 #define new DEBUG_NEW
34 #endif
36 namespace NL3D
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)
52 _Owner= owner;
53 _PatchQuadBlocks.reserve(_StartPatchQuadBlockSize);
55 _CurrentBBoxValidity.setHalfSize(CVector::Null);
57 _GroundMode= true;
58 _CeilMode= false;
59 _SnapToRenderedTesselation= true;
61 _LastGPTValid= false;
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)
83 CVector normal;
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)
95 _LastGPTValid= false;
96 return NULL;
100 // Test GPT cache.
101 //==================
102 // If last call was valid, and if same pos (input or output), return cached information
103 if(_LastGPTValid && (pos==_LastGPTPosInput || pos==_LastGPTPosOutput) )
105 // copy from cache.
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.
121 // =================
122 testComputeLandscape(pos);
125 // find possible faces under the entity.
126 // =================
127 CVisualTileDescNode *ptr= _LandscapeQuadGrid.select(pos);
130 // find the better face under the entity.
131 // =================
132 float sqrBestDist= sqr(1000.f);
133 CVector hit;
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.
147 while(ptr)
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;
172 res= hit;
173 sqrBestDist= sqrdist;
179 // Next in the list.
180 ptr= ptr->Next;
184 // found ??
185 if(sqrBestDist<sqr(1000))
187 // copy into cache
188 _LastGPTValid= true;
189 _LastGPTTrianglePatch= testTriangles[bestTriangle];
190 _LastGPTPosInput= pos;
191 _LastGPTPosOutput= res;
192 // and return ptr on cache.
193 return &_LastGPTTrianglePatch;
195 else
197 _LastGPTValid= false;
198 return NULL;
204 // ***************************************************************************
205 bool CVisualCollisionEntity::snapToGround(CVector &pos, CVector &normal)
207 // Get Patch Triangle Under Us
208 CVector res;
209 CTrianglePatch *tri= getPatchTriangleUnderUs(pos, res);
211 // result. NB: if not found, dot not modify.
212 if( tri )
214 if(_SnapToRenderedTesselation)
216 // snap the position to the nearest tesselation.
217 pos= res;
219 // snap the position to the current rendered tesselation.
220 snapToLandscapeCurrentTesselation(pos, *tri);
222 else
224 // just snap to the accurate tile tesselation.
225 pos= res;
229 // compute the normal.
230 normal= (tri->V1-tri->V0)^(tri->V2-tri->V0);
231 normal.normalize();
233 return true;
235 else
236 return false;
240 // ***************************************************************************
241 void CVisualCollisionEntity::computeUvForPos(const CTrianglePatch &tri, const CVector &pos, CUV &uv)
243 // compute UV gradients.
244 CVector Gu;
245 CVector Gv;
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);
248 // interpolate
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.
258 CUV uv;
259 computeUvForPos(tri, pos, uv);
261 // Ask pos to landscape.
262 CVector posLand;
263 posLand= _Owner->_Landscape->getTesselatedPos(tri.PatchId, uv);
265 // just keep Z.
266 pos.z= posLand.z;
270 // ***************************************************************************
271 // TestYoyo. For Precision problem.
272 /*static bool testLine(CVector &p1, CVector &p0, const CVector &pos0)
274 float epsilon= 0.1f;
276 float a,b,c; // 2D cartesian coefficients of line in plane X/Y.
277 float norm;
278 // Line p0-p1.
279 a= -(p1.y-p0.y);
280 b= (p1.x-p0.x);
281 norm= sqrtf(sqr(a) + sqr(b));
282 a/= norm;
283 b/= norm;
284 c= -(p0.x*a + p0.y*b);
285 if( (a*pos0.x + b*pos0.y + c) < -epsilon)
286 return false;
287 else
288 return true;
292 // ***************************************************************************
293 bool CVisualCollisionEntity::triangleIntersect2DGround(CTriangle &tri, const CVector &pos0)
295 CVector &p0= tri.V0;
296 CVector &p1= tri.V1;
297 CVector &p2= tri.V2;
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.
310 // Line p0-p1.
311 a= -(p1.y-p0.y);
312 b= (p1.x-p0.x);
313 c= -(p0.x*a + p0.y*b);
314 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
315 // Line p1-p2.
316 a= -(p2.y-p1.y);
317 b= (p2.x-p1.x);
318 c= -(p1.x*a + p1.y*b);
319 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
320 // Line p2-p0.
321 a= -(p0.y-p2.y);
322 b= (p0.x-p2.x);
323 c= -(p2.x*a + p2.y*b);
324 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
326 return true;
329 // ***************************************************************************
330 bool CVisualCollisionEntity::triangleIntersect2DCeil(CTriangle &tri, const CVector &pos0)
332 CVector &p0= tri.V0;
333 CVector &p1= tri.V1;
334 CVector &p2= tri.V2;
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.
340 // Line p0-p1.
341 a= -(p1.y-p0.y);
342 b= (p1.x-p0.x);
343 c= -(p0.x*a + p0.y*b);
344 if( (a*pos0.x + b*pos0.y + c) > 0) return false;
345 // Line p1-p2.
346 a= -(p2.y-p1.y);
347 b= (p2.x-p1.x);
348 c= -(p1.x*a + p1.y*b);
349 if( (a*pos0.x + b*pos0.y + c) > 0) return false;
350 // Line p2-p0.
351 a= -(p0.y-p2.y);
352 b= (p0.x-p2.x);
353 c= -(p2.x*a + p2.y*b);
354 if( (a*pos0.x + b*pos0.y + c) > 0) return false;
356 return true;
359 // ***************************************************************************
360 bool CVisualCollisionEntity::triangleIntersect(CTriangle &tri, const CVector &pos0, const CVector &pos1, CVector &hit)
362 CVector &p0= tri.V0;
363 CVector &p1= tri.V1;
364 CVector &p2= tri.V2;
366 bool ok= false;
367 if( _GroundMode && triangleIntersect2DGround(tri, pos0) )
368 ok= true;
369 if(!ok && _CeilMode && triangleIntersect2DCeil(tri, pos0) )
370 ok= true;
371 if(!ok)
372 return false;
375 // Compute the possible height.
376 CVector tmp;
377 // build the plane
378 CPlane plane;
379 plane.make (p0, p1, p2);
380 // intersect the vertical line with the plane.
381 tmp= plane.intersect(pos0, pos1);
383 float h= tmp.z;
384 // Test if it would fit in the wanted field.
385 if(h>pos1.z) return false;
386 if(h<pos0.z) return false;
388 // OK!!
389 // For cache completness, ensure that X and Y don't move, take same XY than pos0
390 hit.x= pos0.x;
391 hit.y= pos0.y;
392 hit.z= h;
393 return true;
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)
410 sint i;
412 // setup new bbox.
413 //==================
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.
424 //==================
425 _Owner->_Landscape->buildPatchBlocksInBBox(bboxToIncludePatchs, _TmpBlockIds);
429 // Recompute PatchQuadBlockcs.
430 //==================
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.
444 uint iOld=0;
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.
452 bool oldEnd= false;
453 CPatchBlockIdent oldBi;
454 if(iOld==_TmpPatchQuadBlocks.size())
455 oldEnd= true;
456 else
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.
469 i++;
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.
478 i++;
479 iOld++;
481 // else, this old block is no longer used, delete it.
482 else
484 _Owner->deletePatchQuadBlock(_TmpPatchQuadBlocks[iOld]);
485 // next old patch block.
486 iOld++;
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.
498 //==================
500 // Compute a delta so elt position for CLandscapeCollisionGrid are positive, and so fastFloor() used will work.
501 CVector delta;
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);
506 delta.z= 0;
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.
511 delta.x+= val;
512 delta.y+= val;
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
525 CVector res;
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.
532 if( tri )
534 // compute UV for position.
535 CUV uv;
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);
546 return true;
548 else
550 // Suppose full Sun Contribution, and don't add any pointLight
551 sunContribution= 255;
553 return false;
558 // ***************************************************************************
559 bool CVisualCollisionEntity::getSurfaceInfo(const CVector &pos, CSurfaceInfo &surfaceInfo)
561 H_AUTO( NL3D_CVisualCollisionEntity_getSurfaceInfo )
562 // Get Patch Triangle Under Us
563 CVector res;
564 CTrianglePatch *tri= getPatchTriangleUnderUs(pos, res);
566 // result. NB: if not found, dot not modify.
567 if( tri )
569 // compute UV for position.
570 CUV uv;
571 computeUvForPos(*tri, pos, uv);
573 // get the tileelement
574 CTileElement *tileElm = _Owner->_Landscape->getTileElement(tri->PatchId, uv);
575 if (tileElm)
577 // Valid tile ?
578 uint16 tileId = tileElm->Tile[0];
579 if (tileId != NL_TILE_ELM_LAYER_EMPTY)
581 // The tilebank
582 CTileBank &tileBank = _Owner->_Landscape->TileBank;
584 // Get xref info for this tile
585 int tileSet;
586 int number;
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);
593 // Fill the surface
594 surfaceInfo.UserSurfaceData = tileSetPtr->SurfaceData;
596 // Ok
597 return true;
601 return false;
605 // ***************************************************************************
606 void CVisualCollisionEntity::displayDebugGrid(IDriver &drv) const
608 // static to not reallocate each frame
609 static CMaterial mat;
610 static bool inited= false;
611 if(!inited)
613 inited= true;
614 mat.initUnlit();
616 static vector<CLine> lineList;
617 lineList.clear();
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);
632 uint sd1= sd0+1;
633 uint td1= td0+1;
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];
640 // build the 5 lines
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);
656 } // NL3D