2012-04-09 14:51:03 +02:00
/*
2014-01-14 12:38:19 +01:00
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
2012-04-09 14:51:03 +02:00
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2012-08-25 21:55:03 -05:00
#include "PathGenerator.h"
2012-04-09 14:51:03 +02:00
#include "Map.h"
#include "Creature.h"
#include "MMapFactory.h"
#include "MMapManager.h"
#include "Log.h"
#include "DetourCommon.h"
#include "DetourNavMeshQuery.h"
2013-10-05 18:11:39 -05:00
float PathGenerator :: MinWallDistance = 2.5f ;
2012-08-25 21:55:03 -05:00
////////////////// PathGenerator //////////////////
PathGenerator :: PathGenerator ( const Unit * owner ) :
2013-10-04 22:23:17 -05:00
_type ( PATHFIND_BLANK ), _endPosition ( G3D :: Vector3 :: zero ()),
_sourceUnit ( owner ), _navMesh ( NULL ), _navMeshQuery ( NULL )
2012-04-09 14:51:03 +02:00
{
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::PathGenerator for %u \n " , _sourceUnit -> GetGUIDLow ());
2012-04-09 14:51:03 +02:00
2012-09-05 23:47:02 +02:00
uint32 mapId = _sourceUnit -> GetMapId ();
2012-04-09 14:51:03 +02:00
if ( MMAP :: MMapFactory :: IsPathfindingEnabled ( mapId ))
{
2013-10-03 17:47:38 -05:00
MMAP :: MMapManager * mmap = MMAP :: MMapFactory :: CreateOrGetMMapManager ();
2012-09-05 23:47:02 +02:00
_navMesh = mmap -> GetNavMesh ( mapId );
_navMeshQuery = mmap -> GetNavMeshQuery ( mapId , _sourceUnit -> GetInstanceId ());
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
CreateFilter ();
2012-04-09 14:51:03 +02:00
}
2012-08-25 21:55:03 -05:00
PathGenerator ::~ PathGenerator ()
2012-04-09 14:51:03 +02:00
{
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::~PathGenerator() for %u \n " , _sourceUnit -> GetGUIDLow ());
2012-04-09 14:51:03 +02:00
}
2012-08-25 21:55:03 -05:00
bool PathGenerator :: CalculatePath ( float destX , float destY , float destZ , bool forceDest )
2012-04-09 14:51:03 +02:00
{
2013-12-30 18:47:34 -05:00
// Clear the previous path, just in case that the same PathGenerator instance is being used
_pathPoints . clear ();
2012-09-04 23:33:11 +02:00
float x , y , z ;
2012-09-05 23:47:02 +02:00
_sourceUnit -> GetPosition ( x , y , z );
2012-09-04 23:33:11 +02:00
if ( ! Trinity :: IsValidMapCoord ( destX , destY , destZ ) || ! Trinity :: IsValidMapCoord ( x , y , z ))
2013-12-30 15:19:50 -05:00
{
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() called with invalid map coords, destX: %f destY: %f destZ: %f x: %f y: %f z: %f for creature %u" , destX , destY , destZ , x , y , z , _sourceUnit -> GetGUIDLow ());
2013-12-30 18:47:34 -05:00
_type = PATHFIND_NOPATH ;
2012-04-09 14:51:03 +02:00
return false ;
2013-12-30 15:19:50 -05:00
}
2012-04-09 14:51:03 +02:00
2013-05-07 12:02:22 +02:00
G3D :: Vector3 dest ( destX , destY , destZ );
2012-09-05 23:47:02 +02:00
SetEndPosition ( dest );
2012-04-09 14:51:03 +02:00
2013-05-07 12:02:22 +02:00
G3D :: Vector3 start ( x , y , z );
2012-09-05 23:47:02 +02:00
SetStartPosition ( start );
2012-04-09 14:51:03 +02:00
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() for %u \n " , _sourceUnit -> GetGUIDLow ());
2012-04-09 14:51:03 +02:00
// make sure navMesh works - we can run on map w/o mmap
// check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
2013-10-04 22:23:17 -05:00
if ( ! _navMesh || ! _navMeshQuery || _sourceUnit -> HasUnitState ( UNIT_STATE_IGNORE_PATHFINDING ))
2012-04-09 14:51:03 +02:00
{
2013-12-30 15:19:50 -05:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() navmesh is not initialized for %u \n " , _sourceUnit -> GetGUIDLow ());
2012-09-05 23:47:02 +02:00
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
2013-12-30 18:47:34 -05:00
_pathPoints . push_back ( start );
_pathPoints . push_back ( dest );
2012-04-09 14:51:03 +02:00
return true ;
}
2012-09-05 23:47:02 +02:00
UpdateFilter ();
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
float startPos [ 3 ];
startPos [ 0 ] = - y ;
startPos [ 1 ] = z ;
startPos [ 2 ] = - x ;
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
float endPos [ 3 ];
endPos [ 0 ] = - destY ;
endPos [ 1 ] = destZ ;
endPos [ 2 ] = - destX ;
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
float polyPickExt [ 3 ];
polyPickExt [ 0 ] = 2.5f ;
polyPickExt [ 1 ] = 2.5f ;
polyPickExt [ 2 ] = 2.5f ;
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
//
dtPolyRef startRef ;
dtPolyRef endRef ;
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
float nearestPt [ 3 ];
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
_navMeshQuery -> findNearestPoly ( startPos , polyPickExt , & _filter , & startRef , nearestPt );
_navMeshQuery -> findNearestPoly ( endPos , polyPickExt , & _filter , & endRef , nearestPt );
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
if ( ! startRef || ! endRef )
2012-04-09 14:51:03 +02:00
{
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() for %u no polygons found for start and end locations \n " , _sourceUnit -> GetGUIDLow ());
2013-12-30 18:47:34 -05:00
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
_pathPoints . push_back ( start );
_pathPoints . push_back ( dest );
2013-10-04 22:23:17 -05:00
return false ;
2012-04-09 14:51:03 +02:00
}
2013-10-04 22:23:17 -05:00
int hops ;
dtPolyRef * hopBuffer = new dtPolyRef [ 8192 ];
dtStatus status = _navMeshQuery -> findPath ( startRef , endRef , startPos , endPos , & _filter , hopBuffer , & hops , 8192 );
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
if ( ! dtStatusSucceed ( status ))
2012-04-09 14:51:03 +02:00
{
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() for %u no path found for start and end locations \n " , _sourceUnit -> GetGUIDLow ());
2013-12-30 18:47:34 -05:00
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
_pathPoints . push_back ( start );
_pathPoints . push_back ( dest );
2013-10-04 22:23:17 -05:00
return false ;
2012-04-09 14:51:03 +02:00
}
2013-10-04 22:23:17 -05:00
int resultHopCount ;
float * straightPath = new float [ 2048 * 3 ];
unsigned char * pathFlags = new unsigned char [ 2048 ];
dtPolyRef * pathRefs = new dtPolyRef [ 2048 ];
2012-04-09 14:51:03 +02:00
2013-10-04 22:23:17 -05:00
status = _navMeshQuery -> findStraightPath ( startPos , endPos , hopBuffer , hops , straightPath , pathFlags , pathRefs , & resultHopCount , 2048 );
if ( ! dtStatusSucceed ( status ))
2012-04-09 14:51:03 +02:00
{
2013-12-20 17:39:13 +01:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() for %u no straight path found for start and end locations \n " , _sourceUnit -> GetGUIDLow ());
2013-12-30 18:47:34 -05:00
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
_pathPoints . push_back ( start );
_pathPoints . push_back ( dest );
2013-10-04 22:23:17 -05:00
return false ;
2012-09-03 20:30:30 +02:00
}
2012-04-09 14:51:03 +02:00
2013-10-05 18:11:39 -05:00
SmoothPath ( polyPickExt , resultHopCount , straightPath ); // Separate the path from the walls
2013-10-04 22:23:17 -05:00
for ( uint32 i = 0 ; i < resultHopCount ; ++ i )
2013-12-30 15:19:50 -05:00
{
2013-10-04 22:23:17 -05:00
_pathPoints . push_back ( G3D :: Vector3 ( - straightPath [ i * 3 + 2 ], - straightPath [ i * 3 + 0 ], straightPath [ i * 3 + 1 ]));
2013-12-30 15:31:12 -05:00
TC_LOG_DEBUG ( "maps" , "PathGenerator::CalculatePath() for %u path point %u: (%f, %f, %f)" , _sourceUnit -> GetGUIDLow (), i , _pathPoints [ i ]. x , _pathPoints [ i ]. y , _pathPoints [ i ]. z );
2013-12-30 15:19:50 -05:00
}
2012-04-09 14:51:03 +02:00
2013-12-30 18:47:34 -05:00
_type = PATHFIND_NORMAL ;
2013-10-04 22:23:17 -05:00
return true ;
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
void PathGenerator :: CreateFilter ()
2012-04-09 14:51:03 +02:00
{
2013-10-05 14:28:53 -05:00
uint16 includeFlags = POLY_FLAG_WALK | POLY_FLAG_SWIM ;
2012-04-09 14:51:03 +02:00
uint16 excludeFlags = 0 ;
2013-10-04 22:23:17 -05:00
if ( _sourceUnit -> GetTypeId () == TYPEID_UNIT && ! _sourceUnit -> ToCreature () -> CanSwim ())
2012-04-09 14:51:03 +02:00
{
2013-10-05 14:28:53 -05:00
includeFlags = POLY_FLAG_WALK ;
excludeFlags = POLY_FLAG_SWIM ;
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
_filter . setIncludeFlags ( includeFlags );
_filter . setExcludeFlags ( excludeFlags );
2012-04-09 14:51:03 +02:00
2012-09-05 23:47:02 +02:00
UpdateFilter ();
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
void PathGenerator :: UpdateFilter ()
2012-04-09 14:51:03 +02:00
{
2013-10-05 18:11:39 -05:00
}
float PathGenerator :: GetTriangleArea ( float * verts , int nv )
{
float area = 0 ;
for ( int i = 0 ; i < nv - 1 ; i ++ )
area += verts [ i * 3 ] * verts [ i * 3 + 5 ] - verts [ i * 3 + 3 ] * verts [ i * 3 + 2 ];
area += verts [( nv - 1 ) * 3 ] * verts [ 2 ] - verts [ 0 ] * verts [( nv - 1 ) * 3 + 2 ];
return area * 0.5f ;
}
bool PathGenerator :: PointInPoly ( float * pos , float * verts , int nv , float err )
{
// Poly area
float area = abs ( PathGenerator :: GetTriangleArea ( verts , nv ));
// Calculate each area of the triangles
float testTri [ 9 ];
memcpy ( testTri , pos , sizeof ( float ) * 3 );
float area1 = 0 ;
for ( int i = 0 ; i < nv - 1 ; ++ i )
{
memcpy ( & testTri [ 3 ], & verts [ i * 3 ], sizeof ( float ) * 3 );
memcpy ( & testTri [ 6 ], & verts [ i * 3 + 3 ], sizeof ( float ) * 3 );
area1 += abs ( PathGenerator :: GetTriangleArea ( testTri , 3 ));
if ( area1 - err > area )
return false ;
}
// Last one
memcpy ( & testTri [ 3 ], verts , sizeof ( float ) * 3 );
memcpy ( & testTri [ 6 ], & verts [ nv * 3 - 3 ] , sizeof ( float ) * 3 );
area1 += abs ( PathGenerator :: GetTriangleArea ( testTri , 3 ));
return abs ( area1 - area ) < err ;
}
float PathGenerator :: DistanceToWall ( float * polyPickExt , float * pos , float * hitPos , float * hitNormal )
{
float distanceToWall = 0 ;
dtPolyRef ref ;
2012-04-09 14:51:03 +02:00
2013-10-05 18:11:39 -05:00
dtStatus status = _navMeshQuery -> findNearestPoly ( pos , polyPickExt , & _filter , & ref , 0 );
2012-04-09 14:51:03 +02:00
2013-10-05 18:11:39 -05:00
if ( ! dtStatusSucceed ( status ) || ref == 0 )
return - 1 ;
const dtMeshTile * tile = 0 ;
const dtPoly * poly = 0 ;
if ( dtStatusFailed ( _navMesh -> getTileAndPolyByRef ( ref , & tile , & poly )))
return - 1 ;
// Collect vertices.
float verts [ DT_VERTS_PER_POLYGON * 3 ];
int nv = 0 ;
for ( unsigned char i = 0 ; i < poly -> vertCount ; ++ i )
{
dtVcopy ( & verts [ nv * 3 ], & tile -> verts [ poly -> verts [ i ] * 3 ]);
nv ++ ;
2012-04-09 14:51:03 +02:00
}
2013-10-05 18:11:39 -05:00
bool inside = PathGenerator :: PointInPoly ( pos , verts , nv , 0.05f );
if ( ! inside )
return - 1 ;
if ( ! dtStatusSucceed ( _navMeshQuery -> findDistanceToWall ( ref , pos , 100.0f , & _filter , & distanceToWall , hitPos , hitNormal )))
return - 1 ;
return distanceToWall ;
}
void PathGenerator :: SmoothPath ( float * polyPickExt , int pathLength , float *& straightPath )
{
float hitPos [ 3 ];
float hitNormal [ 3 ];
float testPos [ 3 ];
float distanceToWall = 0 ;
float up [] = { 0 , 1 , 0 };
float origDis = 0 ;
for ( int i = 1 ; i < pathLength - 1 ; ++ i )
{
dtPolyRef pt ;
float * curPoi = & straightPath [ i * 3 ];
distanceToWall = DistanceToWall ( polyPickExt , curPoi , hitPos , hitNormal );
if ( distanceToWall < PathGenerator :: MinWallDistance && distanceToWall >= 0 )
{
float vec [ 3 ];
dtVsub ( vec , & straightPath [ i * 3 - 3 ], & straightPath [ i * 3 ]);
// If distanceToWall is 0 means the point is in the edge, so we can't get the hitpos.
if ( distanceToWall == 0 )
{
// Test the left side
dtVcross ( testPos , vec , up );
dtVadd ( testPos , testPos , curPoi );
float ft = PathGenerator :: MinWallDistance / dtVdist ( testPos , curPoi );
dtVlerp ( testPos , curPoi , testPos , ft );
distanceToWall = DistanceToWall ( polyPickExt , testPos , hitPos , hitNormal );
if ( abs ( PathGenerator :: MinWallDistance - distanceToWall ) > 0.1f )
{
// Test the right side
dtVcross ( testPos , up , vec );
dtVadd ( testPos , testPos , curPoi );
ft = PathGenerator :: MinWallDistance / dtVdist ( testPos , curPoi );
dtVlerp ( testPos , curPoi , testPos , ft );
distanceToWall = DistanceToWall ( polyPickExt , testPos , hitPos , hitNormal );
}
// If the test point is better than the orig point, replace it.
if ( abs ( distanceToWall - PathGenerator :: MinWallDistance ) < 0.1f )
dtVcopy ( curPoi , testPos );
}
else
{
// We get the hitpos with a ray
float ft = PathGenerator :: MinWallDistance / distanceToWall ;
dtVlerp ( testPos , hitPos , curPoi , ft );
distanceToWall = DistanceToWall ( polyPickExt , testPos , hitPos , hitNormal );
if ( abs ( distanceToWall - PathGenerator :: MinWallDistance ) < 0.1f )
dtVcopy ( curPoi , testPos );
}
}
}
2012-04-09 14:51:03 +02:00
}