3680 lines
		
	
	
		
			102 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			3680 lines
		
	
	
		
			102 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// This software is provided 'as-is', without any express or implied
							 | 
						||
| 
								 | 
							
								// warranty.  In no event will the authors be held liable for any damages
							 | 
						||
| 
								 | 
							
								// arising from the use of this software.
							 | 
						||
| 
								 | 
							
								// Permission is granted to anyone to use this software for any purpose,
							 | 
						||
| 
								 | 
							
								// including commercial applications, and to alter it and redistribute it
							 | 
						||
| 
								 | 
							
								// freely, subject to the following restrictions:
							 | 
						||
| 
								 | 
							
								// 1. The origin of this software must not be misrepresented; you must not
							 | 
						||
| 
								 | 
							
								//    claim that you wrote the original software. If you use this software
							 | 
						||
| 
								 | 
							
								//    in a product, an acknowledgment in the product documentation would be
							 | 
						||
| 
								 | 
							
								//    appreciated but is not required.
							 | 
						||
| 
								 | 
							
								// 2. Altered source versions must be plainly marked as such, and must not be
							 | 
						||
| 
								 | 
							
								//    misrepresented as being the original software.
							 | 
						||
| 
								 | 
							
								// 3. This notice may not be removed or altered from any source distribution.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <float.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								#include "DetourNavMeshQuery.h"
							 | 
						||
| 
								 | 
							
								#include "DetourNavMesh.h"
							 | 
						||
| 
								 | 
							
								#include "DetourNode.h"
							 | 
						||
| 
								 | 
							
								#include "DetourCommon.h"
							 | 
						||
| 
								 | 
							
								#include "DetourMath.h"
							 | 
						||
| 
								 | 
							
								#include "DetourAlloc.h"
							 | 
						||
| 
								 | 
							
								#include "DetourAssert.h"
							 | 
						||
| 
								 | 
							
								#include <new>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @class dtQueryFilter
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>The Default Implementation</b>
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// At construction: All area costs default to 1.0.  All flags are included
							 | 
						||
| 
								 | 
							
								/// and none are excluded.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If a polygon has both an include and an exclude flag, it will be excluded.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// The way filtering works, a navigation mesh polygon must have at least one flag 
							 | 
						||
| 
								 | 
							
								/// set to ever be considered by a query. So a polygon with no flags will never
							 | 
						||
| 
								 | 
							
								/// be considered.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Setting the include flags to 0 will result in all polygons being excluded.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>Custom Implementations</b>
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// DT_VIRTUAL_QUERYFILTER must be defined in order to extend this class.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// Implement a custom query filter by overriding the virtual passFilter() 
							 | 
						||
| 
								 | 
							
								/// and getCost() functions. If this is done, both functions should be as 
							 | 
						||
| 
								 | 
							
								/// fast as possible. Use cached local copies of data rather than accessing 
							 | 
						||
| 
								 | 
							
								/// your own objects where possible.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// Custom implementations do not need to adhere to the flags or cost logic 
							 | 
						||
| 
								 | 
							
								/// used by the default implementation.  
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// In order for A* searches to work properly, the cost should be proportional to
							 | 
						||
| 
								 | 
							
								/// the travel distance. Implementing a cost modifier less than 1.0 is likely 
							 | 
						||
| 
								 | 
							
								/// to lead to problems during pathfinding.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @see dtNavMeshQuery
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtQueryFilter::dtQueryFilter() :
							 | 
						||
| 
								 | 
							
									m_includeFlags(0xffff),
							 | 
						||
| 
								 | 
							
									m_excludeFlags(0)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < DT_MAX_AREAS; ++i)
							 | 
						||
| 
								 | 
							
										m_areaCost[i] = 1.0f;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef DT_VIRTUAL_QUERYFILTER
							 | 
						||
| 
								 | 
							
								bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
							 | 
						||
| 
								 | 
							
															   const dtMeshTile* /*tile*/,
							 | 
						||
| 
								 | 
							
															   const dtPoly* poly) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								float dtQueryFilter::getCost(const float* pa, const float* pb,
							 | 
						||
| 
								 | 
							
															 const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
							 | 
						||
| 
								 | 
							
															 const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
							 | 
						||
| 
								 | 
							
															 const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
							 | 
						||
| 
								 | 
							
																	  const dtMeshTile* /*tile*/,
							 | 
						||
| 
								 | 
							
																	  const dtPoly* poly) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inline float dtQueryFilter::getCost(const float* pa, const float* pb,
							 | 
						||
| 
								 | 
							
																	const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
							 | 
						||
| 
								 | 
							
																	const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
							 | 
						||
| 
								 | 
							
																	const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif	
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								static const float H_SCALE = 0.999f; // Search heuristic scale.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtNavMeshQuery* dtAllocNavMeshQuery()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM);
							 | 
						||
| 
								 | 
							
									if (!mem) return 0;
							 | 
						||
| 
								 | 
							
									return new(mem) dtNavMeshQuery;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!navmesh) return;
							 | 
						||
| 
								 | 
							
									navmesh->~dtNavMeshQuery();
							 | 
						||
| 
								 | 
							
									dtFree(navmesh);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @class dtNavMeshQuery
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// For methods that support undersized buffers, if the buffer is too small 
							 | 
						||
| 
								 | 
							
								/// to hold the entire result set the return status of the method will include 
							 | 
						||
| 
								 | 
							
								/// the #DT_BUFFER_TOO_SMALL flag.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Constant member functions can be used by multiple clients without side
							 | 
						||
| 
								 | 
							
								/// effects. (E.g. No change to the closed list. No impact on an in-progress
							 | 
						||
| 
								 | 
							
								/// sliced path query. Etc.)
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// Walls and portals: A @e wall is a polygon segment that is 
							 | 
						||
| 
								 | 
							
								/// considered impassable. A @e portal is a passable segment between polygons.
							 | 
						||
| 
								 | 
							
								/// A portal may be treated as a wall based on the dtQueryFilter used for a query.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @see dtNavMesh, dtQueryFilter, #dtAllocNavMeshQuery(), #dtAllocNavMeshQuery()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtNavMeshQuery::dtNavMeshQuery() :
							 | 
						||
| 
								 | 
							
									m_nav(0),
							 | 
						||
| 
								 | 
							
									m_tinyNodePool(0),
							 | 
						||
| 
								 | 
							
									m_nodePool(0),
							 | 
						||
| 
								 | 
							
									m_openList(0)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtNavMeshQuery::~dtNavMeshQuery()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (m_tinyNodePool)
							 | 
						||
| 
								 | 
							
										m_tinyNodePool->~dtNodePool();
							 | 
						||
| 
								 | 
							
									if (m_nodePool)
							 | 
						||
| 
								 | 
							
										m_nodePool->~dtNodePool();
							 | 
						||
| 
								 | 
							
									if (m_openList)
							 | 
						||
| 
								 | 
							
										m_openList->~dtNodeQueue();
							 | 
						||
| 
								 | 
							
									dtFree(m_tinyNodePool);
							 | 
						||
| 
								 | 
							
									dtFree(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtFree(m_openList);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par 
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Must be the first function called after construction, before other
							 | 
						||
| 
								 | 
							
								/// functions are used.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This function can be used multiple times.
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (maxNodes > DT_NULL_IDX || maxNodes > (1 << DT_NODE_PARENT_BITS) - 1)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									m_nav = nav;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										if (m_nodePool)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_nodePool->~dtNodePool();
							 | 
						||
| 
								 | 
							
											dtFree(m_nodePool);
							 | 
						||
| 
								 | 
							
											m_nodePool = 0;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
							 | 
						||
| 
								 | 
							
										if (!m_nodePool)
							 | 
						||
| 
								 | 
							
											return DT_FAILURE | DT_OUT_OF_MEMORY;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (!m_tinyNodePool)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32);
							 | 
						||
| 
								 | 
							
										if (!m_tinyNodePool)
							 | 
						||
| 
								 | 
							
											return DT_FAILURE | DT_OUT_OF_MEMORY;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_tinyNodePool->clear();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (!m_openList || m_openList->getCapacity() < maxNodes)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										if (m_openList)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_openList->~dtNodeQueue();
							 | 
						||
| 
								 | 
							
											dtFree(m_openList);
							 | 
						||
| 
								 | 
							
											m_openList = 0;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes);
							 | 
						||
| 
								 | 
							
										if (!m_openList)
							 | 
						||
| 
								 | 
							
											return DT_FAILURE | DT_OUT_OF_MEMORY;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_openList->clear();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
							 | 
						||
| 
								 | 
							
																		 dtPolyRef* randomRef, float* randomPt) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!filter || !frand || !randomRef || !randomPt)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Randomly pick one tile. Assume that all tiles cover roughly the same area.
							 | 
						||
| 
								 | 
							
									const dtMeshTile* tile = 0;
							 | 
						||
| 
								 | 
							
									float tsum = 0.0f;
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < m_nav->getMaxTiles(); i++)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										const dtMeshTile* t = m_nav->getTile(i);
							 | 
						||
| 
								 | 
							
										if (!t || !t->header) continue;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Choose random tile using reservoi sampling.
							 | 
						||
| 
								 | 
							
										const float area = 1.0f; // Could be tile area too.
							 | 
						||
| 
								 | 
							
										tsum += area;
							 | 
						||
| 
								 | 
							
										const float u = frand();
							 | 
						||
| 
								 | 
							
										if (u*tsum <= area)
							 | 
						||
| 
								 | 
							
											tile = t;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if (!tile)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Randomly pick one polygon weighted by polygon area.
							 | 
						||
| 
								 | 
							
									const dtPoly* poly = 0;
							 | 
						||
| 
								 | 
							
									dtPolyRef polyRef = 0;
							 | 
						||
| 
								 | 
							
									const dtPolyRef base = m_nav->getPolyRefBase(tile);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									float areaSum = 0.0f;
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < tile->header->polyCount; ++i)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										const dtPoly* p = &tile->polys[i];
							 | 
						||
| 
								 | 
							
										// Do not return off-mesh connection polygons.
							 | 
						||
| 
								 | 
							
										if (p->getType() != DT_POLYTYPE_GROUND)
							 | 
						||
| 
								 | 
							
											continue;
							 | 
						||
| 
								 | 
							
										// Must pass filter
							 | 
						||
| 
								 | 
							
										const dtPolyRef ref = base | (dtPolyRef)i;
							 | 
						||
| 
								 | 
							
										if (!filter->passFilter(ref, tile, p))
							 | 
						||
| 
								 | 
							
											continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calc area of the polygon.
							 | 
						||
| 
								 | 
							
										float polyArea = 0.0f;
							 | 
						||
| 
								 | 
							
										for (int j = 2; j < p->vertCount; ++j)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const float* va = &tile->verts[p->verts[0]*3];
							 | 
						||
| 
								 | 
							
											const float* vb = &tile->verts[p->verts[j-1]*3];
							 | 
						||
| 
								 | 
							
											const float* vc = &tile->verts[p->verts[j]*3];
							 | 
						||
| 
								 | 
							
											polyArea += dtTriArea2D(va,vb,vc);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Choose random polygon weighted by area, using reservoi sampling.
							 | 
						||
| 
								 | 
							
										areaSum += polyArea;
							 | 
						||
| 
								 | 
							
										const float u = frand();
							 | 
						||
| 
								 | 
							
										if (u*areaSum <= polyArea)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											poly = p;
							 | 
						||
| 
								 | 
							
											polyRef = ref;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (!poly)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Randomly pick point on polygon.
							 | 
						||
| 
								 | 
							
									const float* v = &tile->verts[poly->verts[0]*3];
							 | 
						||
| 
								 | 
							
									float verts[3*DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									float areas[DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									dtVcopy(&verts[0*3],v);
							 | 
						||
| 
								 | 
							
									for (int j = 1; j < poly->vertCount; ++j)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										v = &tile->verts[poly->verts[j]*3];
							 | 
						||
| 
								 | 
							
										dtVcopy(&verts[j*3],v);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const float s = frand();
							 | 
						||
| 
								 | 
							
									const float t = frand();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float pt[3];
							 | 
						||
| 
								 | 
							
									dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float h = 0.0f;
							 | 
						||
| 
								 | 
							
									dtStatus status = getPolyHeight(polyRef, pt, &h);
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(status))
							 | 
						||
| 
								 | 
							
										return status;
							 | 
						||
| 
								 | 
							
									pt[1] = h;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtVcopy(randomPt, pt);
							 | 
						||
| 
								 | 
							
									*randomRef = polyRef;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float maxRadius,
							 | 
						||
| 
								 | 
							
																					 const dtQueryFilter* filter, float (*frand)(),
							 | 
						||
| 
								 | 
							
																					 dtPolyRef* randomRef, float* randomPt) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!centerPos || !dtVisfinite(centerPos) ||
							 | 
						||
| 
								 | 
							
										maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
							 | 
						||
| 
								 | 
							
										!filter || !frand || !randomRef || !randomPt)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const dtMeshTile* startTile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* startPoly = 0;
							 | 
						||
| 
								 | 
							
									m_nav->getTileAndPolyByRefUnsafe(startRef, &startTile, &startPoly);
							 | 
						||
| 
								 | 
							
									if (!filter->passFilter(startRef, startTile, startPoly))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, centerPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const float radiusSqr = dtSqr(maxRadius);
							 | 
						||
| 
								 | 
							
									float areaSum = 0.0f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const dtMeshTile* randomTile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* randomPoly = 0;
							 | 
						||
| 
								 | 
							
									dtPolyRef randomPolyRef = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									while (!m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Place random locations on on ground.
							 | 
						||
| 
								 | 
							
										if (bestPoly->getType() == DT_POLYTYPE_GROUND)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Calc area of the polygon.
							 | 
						||
| 
								 | 
							
											float polyArea = 0.0f;
							 | 
						||
| 
								 | 
							
											for (int j = 2; j < bestPoly->vertCount; ++j)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const float* va = &bestTile->verts[bestPoly->verts[0]*3];
							 | 
						||
| 
								 | 
							
												const float* vb = &bestTile->verts[bestPoly->verts[j-1]*3];
							 | 
						||
| 
								 | 
							
												const float* vc = &bestTile->verts[bestPoly->verts[j]*3];
							 | 
						||
| 
								 | 
							
												polyArea += dtTriArea2D(va,vb,vc);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											// Choose random polygon weighted by area, using reservoi sampling.
							 | 
						||
| 
								 | 
							
											areaSum += polyArea;
							 | 
						||
| 
								 | 
							
											const float u = frand();
							 | 
						||
| 
								 | 
							
											if (u*areaSum <= polyArea)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												randomTile = bestTile;
							 | 
						||
| 
								 | 
							
												randomPoly = bestPoly;
							 | 
						||
| 
								 | 
							
												randomPolyRef = bestRef;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
											parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &bestTile->links[i];
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = link->ref;
							 | 
						||
| 
								 | 
							
											// Skip invalid neighbours and do not follow back to parent.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Expand to neighbour
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Do not advance if the polygon is excluded by the filter.
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Find edge and calc distance to the edge.
							 | 
						||
| 
								 | 
							
											float va[3], vb[3];
							 | 
						||
| 
								 | 
							
											if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the circle is not touching the next polygon, skip it.
							 | 
						||
| 
								 | 
							
											float tseg;
							 | 
						||
| 
								 | 
							
											float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
							 | 
						||
| 
								 | 
							
											if (distSqr > radiusSqr)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Cost
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
												dtVlerp(neighbourNode->pos, va, vb, 0.5f);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												neighbourNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (!randomPoly)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Randomly pick point on polygon.
							 | 
						||
| 
								 | 
							
									const float* v = &randomTile->verts[randomPoly->verts[0]*3];
							 | 
						||
| 
								 | 
							
									float verts[3*DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									float areas[DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									dtVcopy(&verts[0*3],v);
							 | 
						||
| 
								 | 
							
									for (int j = 1; j < randomPoly->vertCount; ++j)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										v = &randomTile->verts[randomPoly->verts[j]*3];
							 | 
						||
| 
								 | 
							
										dtVcopy(&verts[j*3],v);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const float s = frand();
							 | 
						||
| 
								 | 
							
									const float t = frand();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float pt[3];
							 | 
						||
| 
								 | 
							
									dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float h = 0.0f;
							 | 
						||
| 
								 | 
							
									dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(status))
							 | 
						||
| 
								 | 
							
										return stat;
							 | 
						||
| 
								 | 
							
									pt[1] = h;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtVcopy(randomPt, pt);
							 | 
						||
| 
								 | 
							
									*randomRef = randomPolyRef;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Uses the detail polygons to find the surface height. (Most accurate.)
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @p pos does not have to be within the bounds of the polygon or navigation mesh.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// See closestPointOnPolyBoundary() for a limited but faster option.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(ref) ||
							 | 
						||
| 
								 | 
							
										!pos || !dtVisfinite(pos) ||
							 | 
						||
| 
								 | 
							
										!closest)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									m_nav->closestPointOnPoly(ref, pos, closest, posOverPoly);
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Much faster than closestPointOnPoly().
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the provided position lies within the polygon's xz-bounds (above or below), 
							 | 
						||
| 
								 | 
							
								/// then @p pos and @p closest will be equal.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The height of @p closest will be the polygon boundary.  The height detail is not used.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// @p pos does not have to be within the bounds of the polybon or the navigation mesh.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const dtMeshTile* tile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* poly = 0;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!pos || !dtVisfinite(pos) || !closest)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Collect vertices.
							 | 
						||
| 
								 | 
							
									float verts[DT_VERTS_PER_POLYGON*3];	
							 | 
						||
| 
								 | 
							
									float edged[DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									float edget[DT_VERTS_PER_POLYGON];
							 | 
						||
| 
								 | 
							
									int nv = 0;
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < (int)poly->vertCount; ++i)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
							 | 
						||
| 
								 | 
							
										nv++;
							 | 
						||
| 
								 | 
							
									}		
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget);
							 | 
						||
| 
								 | 
							
									if (inside)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Point is inside the polygon, return the point.
							 | 
						||
| 
								 | 
							
										dtVcopy(closest, pos);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Point is outside the polygon, dtClamp to nearest edge.
							 | 
						||
| 
								 | 
							
										float dmin = edged[0];
							 | 
						||
| 
								 | 
							
										int imin = 0;
							 | 
						||
| 
								 | 
							
										for (int i = 1; i < nv; ++i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if (edged[i] < dmin)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												dmin = edged[i];
							 | 
						||
| 
								 | 
							
												imin = i;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										const float* va = &verts[imin*3];
							 | 
						||
| 
								 | 
							
										const float* vb = &verts[((imin+1)%nv)*3];
							 | 
						||
| 
								 | 
							
										dtVlerp(closest, va, vb, edget[imin]);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds 
							 | 
						||
| 
								 | 
							
								/// of the polygon.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const dtMeshTile* tile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* poly = 0;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!pos || !dtVisfinite2D(pos))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// We used to return success for offmesh connections, but the
							 | 
						||
| 
								 | 
							
									// getPolyHeight in DetourNavMesh does not do this, so special
							 | 
						||
| 
								 | 
							
									// case it here.
							 | 
						||
| 
								 | 
							
									if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										const float* v0 = &tile->verts[poly->verts[0]*3];
							 | 
						||
| 
								 | 
							
										const float* v1 = &tile->verts[poly->verts[1]*3];
							 | 
						||
| 
								 | 
							
										float t;
							 | 
						||
| 
								 | 
							
										dtDistancePtSegSqr2D(pos, v0, v1, t);
							 | 
						||
| 
								 | 
							
										if (height)
							 | 
						||
| 
								 | 
							
											*height = v0[1] + (v1[1] - v0[1])*t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return m_nav->getPolyHeight(tile, poly, pos, height)
							 | 
						||
| 
								 | 
							
										? DT_SUCCESS
							 | 
						||
| 
								 | 
							
										: DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class dtFindNearestPolyQuery : public dtPolyQuery
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									const dtNavMeshQuery* m_query;
							 | 
						||
| 
								 | 
							
									const float* m_center;
							 | 
						||
| 
								 | 
							
									float m_nearestDistanceSqr;
							 | 
						||
| 
								 | 
							
									dtPolyRef m_nearestRef;
							 | 
						||
| 
								 | 
							
									float m_nearestPoint[3];
							 | 
						||
| 
								 | 
							
									bool m_overPoly;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
									dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center)
							 | 
						||
| 
								 | 
							
										: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint(), m_overPoly(false)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtPolyRef nearestRef() const { return m_nearestRef; }
							 | 
						||
| 
								 | 
							
									const float* nearestPoint() const { return m_nearestPoint; }
							 | 
						||
| 
								 | 
							
									bool isOverPoly() const { return m_overPoly; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtIgnoreUnused(polys);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < count; ++i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtPolyRef ref = refs[i];
							 | 
						||
| 
								 | 
							
											float closestPtPoly[3];
							 | 
						||
| 
								 | 
							
											float diff[3];
							 | 
						||
| 
								 | 
							
											bool posOverPoly = false;
							 | 
						||
| 
								 | 
							
											float d;
							 | 
						||
| 
								 | 
							
											m_query->closestPointOnPoly(ref, m_center, closestPtPoly, &posOverPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// If a point is directly over a polygon and closer than
							 | 
						||
| 
								 | 
							
											// climb height, favor that instead of straight line nearest point.
							 | 
						||
| 
								 | 
							
											dtVsub(diff, m_center, closestPtPoly);
							 | 
						||
| 
								 | 
							
											if (posOverPoly)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												d = dtAbs(diff[1]) - tile->header->walkableClimb;
							 | 
						||
| 
								 | 
							
												d = d > 0 ? d*d : 0;			
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												d = dtVlenSqr(diff);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (d < m_nearestDistanceSqr)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												dtVcopy(m_nearestPoint, closestPtPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												m_nearestDistanceSqr = d;
							 | 
						||
| 
								 | 
							
												m_nearestRef = ref;
							 | 
						||
| 
								 | 
							
												m_overPoly = posOverPoly;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par 
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @note If the search box does not intersect any polygons the search will 
							 | 
						||
| 
								 | 
							
								/// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check 
							 | 
						||
| 
								 | 
							
								/// @p nearestRef before using @p nearestPt.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
							 | 
						||
| 
								 | 
							
																		 const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																		 dtPolyRef* nearestRef, float* nearestPt) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return findNearestPoly(center, halfExtents, filter, nearestRef, nearestPt, NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// If center and nearestPt point to an equal position, isOverPoly will be true;
							 | 
						||
| 
								 | 
							
								// however there's also a special case of climb height inside the polygon (see dtFindNearestPolyQuery)
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
							 | 
						||
| 
								 | 
							
																		 const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																		 dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!nearestRef)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// queryPolygons below will check rest of params
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtFindNearestPolyQuery query(this, center);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtStatus status = queryPolygons(center, halfExtents, filter, &query);
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(status))
							 | 
						||
| 
								 | 
							
										return status;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*nearestRef = query.nearestRef();
							 | 
						||
| 
								 | 
							
									// Only override nearestPt if we actually found a poly so the nearest point
							 | 
						||
| 
								 | 
							
									// is valid.
							 | 
						||
| 
								 | 
							
									if (nearestPt && *nearestRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtVcopy(nearestPt, query.nearestPoint());
							 | 
						||
| 
								 | 
							
										if (isOverPoly)
							 | 
						||
| 
								 | 
							
											*isOverPoly = query.isOverPoly();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
							 | 
						||
| 
								 | 
							
																		 const dtQueryFilter* filter, dtPolyQuery* query) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									static const int batchSize = 32;
							 | 
						||
| 
								 | 
							
									dtPolyRef polyRefs[batchSize];
							 | 
						||
| 
								 | 
							
									dtPoly* polys[batchSize];
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (tile->bvTree)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										const dtBVNode* node = &tile->bvTree[0];
							 | 
						||
| 
								 | 
							
										const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
							 | 
						||
| 
								 | 
							
										const float* tbmin = tile->header->bmin;
							 | 
						||
| 
								 | 
							
										const float* tbmax = tile->header->bmax;
							 | 
						||
| 
								 | 
							
										const float qfac = tile->header->bvQuantFactor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calculate quantized box
							 | 
						||
| 
								 | 
							
										unsigned short bmin[3], bmax[3];
							 | 
						||
| 
								 | 
							
										// dtClamp query box to world box.
							 | 
						||
| 
								 | 
							
										float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
							 | 
						||
| 
								 | 
							
										float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
							 | 
						||
| 
								 | 
							
										float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
							 | 
						||
| 
								 | 
							
										float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
							 | 
						||
| 
								 | 
							
										float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
							 | 
						||
| 
								 | 
							
										float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
							 | 
						||
| 
								 | 
							
										// Quantize
							 | 
						||
| 
								 | 
							
										bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
							 | 
						||
| 
								 | 
							
										bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
							 | 
						||
| 
								 | 
							
										bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
							 | 
						||
| 
								 | 
							
										bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
							 | 
						||
| 
								 | 
							
										bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
							 | 
						||
| 
								 | 
							
										bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Traverse tree
							 | 
						||
| 
								 | 
							
										const dtPolyRef base = m_nav->getPolyRefBase(tile);
							 | 
						||
| 
								 | 
							
										while (node < end)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
							 | 
						||
| 
								 | 
							
											const bool isLeafNode = node->i >= 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (isLeafNode && overlap)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												dtPolyRef ref = base | (dtPolyRef)node->i;
							 | 
						||
| 
								 | 
							
												if (filter->passFilter(ref, tile, &tile->polys[node->i]))
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													polyRefs[n] = ref;
							 | 
						||
| 
								 | 
							
													polys[n] = &tile->polys[node->i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if (n == batchSize - 1)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														query->process(tile, polys, polyRefs, batchSize);
							 | 
						||
| 
								 | 
							
														n = 0;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													else
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														n++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (overlap || isLeafNode)
							 | 
						||
| 
								 | 
							
												node++;
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const int escapeIndex = -node->i;
							 | 
						||
| 
								 | 
							
												node += escapeIndex;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										float bmin[3], bmax[3];
							 | 
						||
| 
								 | 
							
										const dtPolyRef base = m_nav->getPolyRefBase(tile);
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < tile->header->polyCount; ++i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtPoly* p = &tile->polys[i];
							 | 
						||
| 
								 | 
							
											// Do not return off-mesh connection polygons.
							 | 
						||
| 
								 | 
							
											if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											// Must pass filter
							 | 
						||
| 
								 | 
							
											const dtPolyRef ref = base | (dtPolyRef)i;
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(ref, tile, p))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											// Calc polygon bounds.
							 | 
						||
| 
								 | 
							
											const float* v = &tile->verts[p->verts[0]*3];
							 | 
						||
| 
								 | 
							
											dtVcopy(bmin, v);
							 | 
						||
| 
								 | 
							
											dtVcopy(bmax, v);
							 | 
						||
| 
								 | 
							
											for (int j = 1; j < p->vertCount; ++j)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												v = &tile->verts[p->verts[j]*3];
							 | 
						||
| 
								 | 
							
												dtVmin(bmin, v);
							 | 
						||
| 
								 | 
							
												dtVmax(bmax, v);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if (dtOverlapBounds(qmin, qmax, bmin, bmax))
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												polyRefs[n] = ref;
							 | 
						||
| 
								 | 
							
												polys[n] = p;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (n == batchSize - 1)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													query->process(tile, polys, polyRefs, batchSize);
							 | 
						||
| 
								 | 
							
													n = 0;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													n++;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Process the last polygons that didn't make a full batch.
							 | 
						||
| 
								 | 
							
									if (n > 0)
							 | 
						||
| 
								 | 
							
										query->process(tile, polys, polyRefs, n);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class dtCollectPolysQuery : public dtPolyQuery
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtPolyRef* m_polys;
							 | 
						||
| 
								 | 
							
									const int m_maxPolys;
							 | 
						||
| 
								 | 
							
									int m_numCollected;
							 | 
						||
| 
								 | 
							
									bool m_overflow;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
									dtCollectPolysQuery(dtPolyRef* polys, const int maxPolys)
							 | 
						||
| 
								 | 
							
										: m_polys(polys), m_maxPolys(maxPolys), m_numCollected(0), m_overflow(false)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int numCollected() const { return m_numCollected; }
							 | 
						||
| 
								 | 
							
									bool overflowed() const { return m_overflow; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtIgnoreUnused(tile);
							 | 
						||
| 
								 | 
							
										dtIgnoreUnused(polys);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										int numLeft = m_maxPolys - m_numCollected;
							 | 
						||
| 
								 | 
							
										int toCopy = count;
							 | 
						||
| 
								 | 
							
										if (toCopy > numLeft)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_overflow = true;
							 | 
						||
| 
								 | 
							
											toCopy = numLeft;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										memcpy(m_polys + m_numCollected, refs, (size_t)toCopy * sizeof(dtPolyRef));
							 | 
						||
| 
								 | 
							
										m_numCollected += toCopy;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par 
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If no polygons are found, the function will return #DT_SUCCESS with a
							 | 
						||
| 
								 | 
							
								/// @p polyCount of zero.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If @p polys is too small to hold the entire result set, then the array will 
							 | 
						||
| 
								 | 
							
								/// be filled to capacity. The method of choosing which polygons from the 
							 | 
						||
| 
								 | 
							
								/// full set are included in the partial result set is undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents,
							 | 
						||
| 
								 | 
							
																	   const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																	   dtPolyRef* polys, int* polyCount, const int maxPolys) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!polys || !polyCount || maxPolys < 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtCollectPolysQuery collector(polys, maxPolys);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtStatus status = queryPolygons(center, halfExtents, filter, &collector);
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(status))
							 | 
						||
| 
								 | 
							
										return status;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*polyCount = collector.numCollected();
							 | 
						||
| 
								 | 
							
									return collector.overflowed() ? DT_SUCCESS | DT_BUFFER_TOO_SMALL : DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par 
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The query will be invoked with batches of polygons. Polygons passed
							 | 
						||
| 
								 | 
							
								/// to the query have bounding boxes that overlap with the center and halfExtents
							 | 
						||
| 
								 | 
							
								/// passed to this function. The dtPolyQuery::process function is invoked multiple
							 | 
						||
| 
								 | 
							
								/// times until all overlapping polygons have been processed.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents,
							 | 
						||
| 
								 | 
							
																	   const dtQueryFilter* filter, dtPolyQuery* query) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!center || !dtVisfinite(center) ||
							 | 
						||
| 
								 | 
							
										!halfExtents || !dtVisfinite(halfExtents) ||
							 | 
						||
| 
								 | 
							
										!filter || !query)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									float bmin[3], bmax[3];
							 | 
						||
| 
								 | 
							
									dtVsub(bmin, center, halfExtents);
							 | 
						||
| 
								 | 
							
									dtVadd(bmax, center, halfExtents);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Find tiles the query touches.
							 | 
						||
| 
								 | 
							
									int minx, miny, maxx, maxy;
							 | 
						||
| 
								 | 
							
									m_nav->calcTileLoc(bmin, &minx, &miny);
							 | 
						||
| 
								 | 
							
									m_nav->calcTileLoc(bmax, &maxx, &maxy);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static const int MAX_NEIS = 32;
							 | 
						||
| 
								 | 
							
									const dtMeshTile* neis[MAX_NEIS];
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									for (int y = miny; y <= maxy; ++y)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										for (int x = minx; x <= maxx; ++x)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const int nneis = m_nav->getTilesAt(x,y,neis,MAX_NEIS);
							 | 
						||
| 
								 | 
							
											for (int j = 0; j < nneis; ++j)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												queryPolygonsInTile(neis[j], bmin, bmax, filter, query);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the end polygon cannot be reached through the navigation graph,
							 | 
						||
| 
								 | 
							
								/// the last polygon in the path will be the nearest the end polygon.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the path array is to small to hold the full result, it will be filled as 
							 | 
						||
| 
								 | 
							
								/// far as possible from the start polygon toward the end polygon.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The start and end positions are used to calculate traversal costs. 
							 | 
						||
| 
								 | 
							
								/// (The y-values impact the result.)
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
							 | 
						||
| 
								 | 
							
																  const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																  const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																  dtPolyRef* path, int* pathCount, const int maxPath) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!pathCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*pathCount = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
							 | 
						||
| 
								 | 
							
										!startPos || !dtVisfinite(startPos) ||
							 | 
						||
| 
								 | 
							
										!endPos || !dtVisfinite(endPos) ||
							 | 
						||
| 
								 | 
							
										!filter || !path || maxPath <= 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (startRef == endRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										path[0] = startRef;
							 | 
						||
| 
								 | 
							
										*pathCount = 1;
							 | 
						||
| 
								 | 
							
										return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, startPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = dtVdist(startPos, endPos) * H_SCALE;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* lastBestNode = startNode;
							 | 
						||
| 
								 | 
							
									float lastBestNodeCost = startNode->total;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									bool outOfNodes = false;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (!m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Remove node from open list and put it in closed list.
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Reached the goal, stop searching.
							 | 
						||
| 
								 | 
							
										if (bestNode->id == endRef)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											lastBestNode = bestNode;
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get current poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
											parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = bestTile->links[i].ref;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip invalid ids and do not expand back to where we came from.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Get neighbour poly and tile.
							 | 
						||
| 
								 | 
							
											// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);			
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// deal explicitly with crossing tile boundaries
							 | 
						||
| 
								 | 
							
											unsigned char crossSide = 0;
							 | 
						||
| 
								 | 
							
											if (bestTile->links[i].side != 0xff)
							 | 
						||
| 
								 | 
							
												crossSide = bestTile->links[i].side >> 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// get the node
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												outOfNodes = true;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the node is visited the first time, calculate node position.
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												getEdgeMidPoint(bestRef, bestPoly, bestTile,
							 | 
						||
| 
								 | 
							
																neighbourRef, neighbourPoly, neighbourTile,
							 | 
						||
| 
								 | 
							
																neighbourNode->pos);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Calculate cost and heuristic.
							 | 
						||
| 
								 | 
							
											float cost = 0;
							 | 
						||
| 
								 | 
							
											float heuristic = 0;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Special case for last node.
							 | 
						||
| 
								 | 
							
											if (neighbourRef == endRef)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Cost
							 | 
						||
| 
								 | 
							
												const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
							 | 
						||
| 
								 | 
							
																					  parentRef, parentTile, parentPoly,
							 | 
						||
| 
								 | 
							
																					  bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
																					  neighbourRef, neighbourTile, neighbourPoly);
							 | 
						||
| 
								 | 
							
												const float endCost = filter->getCost(neighbourNode->pos, endPos,
							 | 
						||
| 
								 | 
							
																					  bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
																					  neighbourRef, neighbourTile, neighbourPoly,
							 | 
						||
| 
								 | 
							
																					  0, 0, 0);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												cost = bestNode->cost + curCost + endCost;
							 | 
						||
| 
								 | 
							
												heuristic = 0;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Cost
							 | 
						||
| 
								 | 
							
												const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
							 | 
						||
| 
								 | 
							
																					  parentRef, parentTile, parentPoly,
							 | 
						||
| 
								 | 
							
																					  bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
																					  neighbourRef, neighbourTile, neighbourPoly);
							 | 
						||
| 
								 | 
							
												cost = bestNode->cost + curCost;
							 | 
						||
| 
								 | 
							
												heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const float total = cost + heuristic;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											// The node is already visited and process, and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Add or update the node.
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
							 | 
						||
| 
								 | 
							
											neighbourNode->cost = cost;
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Already in open, update node location.
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Put the node in open list.
							 | 
						||
| 
								 | 
							
												neighbourNode->flags |= DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Update nearest node to target so far.
							 | 
						||
| 
								 | 
							
											if (heuristic < lastBestNodeCost)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												lastBestNodeCost = heuristic;
							 | 
						||
| 
								 | 
							
												lastBestNode = neighbourNode;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtStatus status = getPathToNode(lastBestNode, path, pathCount, maxPath);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (lastBestNode->id != endRef)
							 | 
						||
| 
								 | 
							
										status |= DT_PARTIAL_RESULT;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (outOfNodes)
							 | 
						||
| 
								 | 
							
										status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPathToNode(dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// Find the length of the entire path.
							 | 
						||
| 
								 | 
							
									dtNode* curNode = endNode;
							 | 
						||
| 
								 | 
							
									int length = 0;
							 | 
						||
| 
								 | 
							
									do
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										length++;
							 | 
						||
| 
								 | 
							
										curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
							 | 
						||
| 
								 | 
							
									} while (curNode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// If the path cannot be fully stored then advance to the last node we will be able to store.
							 | 
						||
| 
								 | 
							
									curNode = endNode;
							 | 
						||
| 
								 | 
							
									int writeCount;
							 | 
						||
| 
								 | 
							
									for (writeCount = length; writeCount > maxPath; writeCount--)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtAssert(curNode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Write path
							 | 
						||
| 
								 | 
							
									for (int i = writeCount - 1; i >= 0; i--)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtAssert(curNode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										path[i] = curNode->id;
							 | 
						||
| 
								 | 
							
										curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtAssert(!curNode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*pathCount = dtMin(length, maxPath);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (length > maxPath)
							 | 
						||
| 
								 | 
							
										return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @warning Calling any non-slice methods before calling finalizeSlicedFindPath() 
							 | 
						||
| 
								 | 
							
								/// or finalizeSlicedFindPathPartial() may result in corrupted data!
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The @p filter pointer is stored and used for the duration of the sliced
							 | 
						||
| 
								 | 
							
								/// path query.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
							 | 
						||
| 
								 | 
							
																			const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																			const dtQueryFilter* filter, const unsigned int options)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Init path state.
							 | 
						||
| 
								 | 
							
									memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
									m_query.status = DT_FAILURE;
							 | 
						||
| 
								 | 
							
									m_query.startRef = startRef;
							 | 
						||
| 
								 | 
							
									m_query.endRef = endRef;
							 | 
						||
| 
								 | 
							
									if (startPos)
							 | 
						||
| 
								 | 
							
										dtVcopy(m_query.startPos, startPos);
							 | 
						||
| 
								 | 
							
									if (endPos)
							 | 
						||
| 
								 | 
							
										dtVcopy(m_query.endPos, endPos);
							 | 
						||
| 
								 | 
							
									m_query.filter = filter;
							 | 
						||
| 
								 | 
							
									m_query.options = options;
							 | 
						||
| 
								 | 
							
									m_query.raycastLimitSqr = FLT_MAX;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
							 | 
						||
| 
								 | 
							
										!startPos || !dtVisfinite(startPos) ||
							 | 
						||
| 
								 | 
							
										!endPos || !dtVisfinite(endPos) || !filter)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// trade quality with performance?
							 | 
						||
| 
								 | 
							
									if (options & DT_FINDPATH_ANY_ANGLE)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// limiting to several times the character radius yields nice results. It is not sensitive 
							 | 
						||
| 
								 | 
							
										// so it is enough to compute it from the first tile.
							 | 
						||
| 
								 | 
							
										const dtMeshTile* tile = m_nav->getTileByRef(startRef);
							 | 
						||
| 
								 | 
							
										float agentRadius = tile->header->walkableRadius;
							 | 
						||
| 
								 | 
							
										m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (startRef == endRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_query.status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
										return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, startPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = dtVdist(startPos, endPos) * H_SCALE;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_query.status = DT_IN_PROGRESS;
							 | 
						||
| 
								 | 
							
									m_query.lastBestNode = startNode;
							 | 
						||
| 
								 | 
							
									m_query.lastBestNodeCost = startNode->total;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return m_query.status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!dtStatusInProgress(m_query.status))
							 | 
						||
| 
								 | 
							
										return m_query.status;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Make sure the request is still valid.
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef))
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_query.status = DT_FAILURE;
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtRaycastHit rayHit;
							 | 
						||
| 
								 | 
							
									rayHit.maxPath = 0;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
									int iter = 0;
							 | 
						||
| 
								 | 
							
									while (iter < maxIter && !m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										iter++;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Remove node from open list and put it in closed list.
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Reached the goal, stop searching.
							 | 
						||
| 
								 | 
							
										if (bestNode->id == m_query.endRef)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_query.lastBestNode = bestNode;
							 | 
						||
| 
								 | 
							
											const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
											m_query.status = DT_SUCCESS | details;
							 | 
						||
| 
								 | 
							
											if (doneIters)
							 | 
						||
| 
								 | 
							
												*doneIters = iter;
							 | 
						||
| 
								 | 
							
											return m_query.status;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get current poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										if (dtStatusFailed(m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly)))
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// The polygon has disappeared during the sliced query, fail.
							 | 
						||
| 
								 | 
							
											m_query.status = DT_FAILURE;
							 | 
						||
| 
								 | 
							
											if (doneIters)
							 | 
						||
| 
								 | 
							
												*doneIters = iter;
							 | 
						||
| 
								 | 
							
											return m_query.status;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent and grand parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0, grandpaRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										dtNode* parentNode = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx);
							 | 
						||
| 
								 | 
							
											parentRef = parentNode->id;
							 | 
						||
| 
								 | 
							
											if (parentNode->pidx)
							 | 
						||
| 
								 | 
							
												grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly));
							 | 
						||
| 
								 | 
							
											if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) )
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// The polygon has disappeared during the sliced query, fail.
							 | 
						||
| 
								 | 
							
												m_query.status = DT_FAILURE;
							 | 
						||
| 
								 | 
							
												if (doneIters)
							 | 
						||
| 
								 | 
							
													*doneIters = iter;
							 | 
						||
| 
								 | 
							
												return m_query.status;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// decide whether to test raycast to previous nodes
							 | 
						||
| 
								 | 
							
										bool tryLOS = false;
							 | 
						||
| 
								 | 
							
										if (m_query.options & DT_FINDPATH_ANY_ANGLE)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr))
							 | 
						||
| 
								 | 
							
												tryLOS = true;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = bestTile->links[i].ref;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip invalid ids and do not expand back to where we came from.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Get neighbour poly and tile.
							 | 
						||
| 
								 | 
							
											// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);			
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// get the neighbor node
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, 0);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_query.status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// do not expand to nodes that were already visited from the same parent
							 | 
						||
| 
								 | 
							
											if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// If the node is visited the first time, calculate node position.
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												getEdgeMidPoint(bestRef, bestPoly, bestTile,
							 | 
						||
| 
								 | 
							
																neighbourRef, neighbourPoly, neighbourTile,
							 | 
						||
| 
								 | 
							
																neighbourNode->pos);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Calculate cost and heuristic.
							 | 
						||
| 
								 | 
							
											float cost = 0;
							 | 
						||
| 
								 | 
							
											float heuristic = 0;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// raycast parent
							 | 
						||
| 
								 | 
							
											bool foundShortCut = false;
							 | 
						||
| 
								 | 
							
											rayHit.pathCost = rayHit.t = 0;
							 | 
						||
| 
								 | 
							
											if (tryLOS)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef);
							 | 
						||
| 
								 | 
							
												foundShortCut = rayHit.t >= 1.0f;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// update move cost
							 | 
						||
| 
								 | 
							
											if (foundShortCut)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// shortcut found using raycast. Using shorter cost instead
							 | 
						||
| 
								 | 
							
												cost = parentNode->cost + rayHit.pathCost;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// No shortcut found.
							 | 
						||
| 
								 | 
							
												const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
							 | 
						||
| 
								 | 
							
																							  parentRef, parentTile, parentPoly,
							 | 
						||
| 
								 | 
							
																							bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
																							neighbourRef, neighbourTile, neighbourPoly);
							 | 
						||
| 
								 | 
							
												cost = bestNode->cost + curCost;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Special case for last node.
							 | 
						||
| 
								 | 
							
											if (neighbourRef == m_query.endRef)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
							 | 
						||
| 
								 | 
							
																							  bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
																							  neighbourRef, neighbourTile, neighbourPoly,
							 | 
						||
| 
								 | 
							
																							  0, 0, 0);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												cost = cost + endCost;
							 | 
						||
| 
								 | 
							
												heuristic = 0;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											const float total = cost + heuristic;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											// The node is already visited and process, and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Add or update the node.
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED));
							 | 
						||
| 
								 | 
							
											neighbourNode->cost = cost;
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
											if (foundShortCut)
							 | 
						||
| 
								 | 
							
												neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Already in open, update node location.
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Put the node in open list.
							 | 
						||
| 
								 | 
							
												neighbourNode->flags |= DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Update nearest node to target so far.
							 | 
						||
| 
								 | 
							
											if (heuristic < m_query.lastBestNodeCost)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_query.lastBestNodeCost = heuristic;
							 | 
						||
| 
								 | 
							
												m_query.lastBestNode = neighbourNode;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Exhausted all nodes, but could not find path.
							 | 
						||
| 
								 | 
							
									if (m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
										m_query.status = DT_SUCCESS | details;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (doneIters)
							 | 
						||
| 
								 | 
							
										*doneIters = iter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return m_query.status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!pathCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*pathCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!path || maxPath <= 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_query.status))
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Reset query.
							 | 
						||
| 
								 | 
							
										memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (m_query.startRef == m_query.endRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Special case: the search starts and ends at same poly.
							 | 
						||
| 
								 | 
							
										path[n++] = m_query.startRef;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Reverse the path.
							 | 
						||
| 
								 | 
							
										dtAssert(m_query.lastBestNode);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if (m_query.lastBestNode->id != m_query.endRef)
							 | 
						||
| 
								 | 
							
											m_query.status |= DT_PARTIAL_RESULT;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										dtNode* prev = 0;
							 | 
						||
| 
								 | 
							
										dtNode* node = m_query.lastBestNode;
							 | 
						||
| 
								 | 
							
										int prevRay = 0;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
											node->pidx = m_nodePool->getNodeIdx(prev);
							 | 
						||
| 
								 | 
							
											prev = node;
							 | 
						||
| 
								 | 
							
											int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
							 | 
						||
| 
								 | 
							
											node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
							 | 
						||
| 
								 | 
							
											prevRay = nextRay;
							 | 
						||
| 
								 | 
							
											node = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Store path
							 | 
						||
| 
								 | 
							
										node = prev;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
											dtStatus status = 0;
							 | 
						||
| 
								 | 
							
											if (node->flags & DT_NODE_PARENT_DETACHED)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												float t, normal[3];
							 | 
						||
| 
								 | 
							
												int m;
							 | 
						||
| 
								 | 
							
												status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
							 | 
						||
| 
								 | 
							
												n += m;
							 | 
						||
| 
								 | 
							
												// raycast ends on poly boundary and the path might include the next poly boundary.
							 | 
						||
| 
								 | 
							
												if (path[n-1] == next->id)
							 | 
						||
| 
								 | 
							
													n--; // remove to avoid duplicates
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												path[n++] = node->id;
							 | 
						||
| 
								 | 
							
												if (n >= maxPath)
							 | 
						||
| 
								 | 
							
													status = DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (status & DT_STATUS_DETAIL_MASK)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_query.status |= status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											node = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Reset query.
							 | 
						||
| 
								 | 
							
									memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*pathCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS | details;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
							 | 
						||
| 
								 | 
							
																					   dtPolyRef* path, int* pathCount, const int maxPath)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!pathCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*pathCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_query.status))
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Reset query.
							 | 
						||
| 
								 | 
							
										memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
										return DT_FAILURE;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (m_query.startRef == m_query.endRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Special case: the search starts and ends at same poly.
							 | 
						||
| 
								 | 
							
										path[n++] = m_query.startRef;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Find furthest existing node that was visited.
							 | 
						||
| 
								 | 
							
										dtNode* prev = 0;
							 | 
						||
| 
								 | 
							
										dtNode* node = 0;
							 | 
						||
| 
								 | 
							
										for (int i = existingSize-1; i >= 0; --i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_nodePool->findNodes(existing[i], &node, 1);
							 | 
						||
| 
								 | 
							
											if (node)
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if (!node)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											m_query.status |= DT_PARTIAL_RESULT;
							 | 
						||
| 
								 | 
							
											dtAssert(m_query.lastBestNode);
							 | 
						||
| 
								 | 
							
											node = m_query.lastBestNode;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Reverse the path.
							 | 
						||
| 
								 | 
							
										int prevRay = 0;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
											node->pidx = m_nodePool->getNodeIdx(prev);
							 | 
						||
| 
								 | 
							
											prev = node;
							 | 
						||
| 
								 | 
							
											int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
							 | 
						||
| 
								 | 
							
											node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
							 | 
						||
| 
								 | 
							
											prevRay = nextRay;
							 | 
						||
| 
								 | 
							
											node = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Store path
							 | 
						||
| 
								 | 
							
										node = prev;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
											dtStatus status = 0;
							 | 
						||
| 
								 | 
							
											if (node->flags & DT_NODE_PARENT_DETACHED)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												float t, normal[3];
							 | 
						||
| 
								 | 
							
												int m;
							 | 
						||
| 
								 | 
							
												status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
							 | 
						||
| 
								 | 
							
												n += m;
							 | 
						||
| 
								 | 
							
												// raycast ends on poly boundary and the path might include the next poly boundary.
							 | 
						||
| 
								 | 
							
												if (path[n-1] == next->id)
							 | 
						||
| 
								 | 
							
													n--; // remove to avoid duplicates
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												path[n++] = node->id;
							 | 
						||
| 
								 | 
							
												if (n >= maxPath)
							 | 
						||
| 
								 | 
							
													status = DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (status & DT_STATUS_DETAIL_MASK)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_query.status |= status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											node = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Reset query.
							 | 
						||
| 
								 | 
							
									memset(&m_query, 0, sizeof(dtQueryData));
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*pathCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS | details;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref,
							 | 
						||
| 
								 | 
							
																	  float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
							 | 
						||
| 
								 | 
							
																	  int* straightPathCount, const int maxStraightPath) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if ((*straightPathCount) > 0 && dtVequal(&straightPath[((*straightPathCount)-1)*3], pos))
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// The vertices are equal, update flags and poly.
							 | 
						||
| 
								 | 
							
										if (straightPathFlags)
							 | 
						||
| 
								 | 
							
											straightPathFlags[(*straightPathCount)-1] = flags;
							 | 
						||
| 
								 | 
							
										if (straightPathRefs)
							 | 
						||
| 
								 | 
							
											straightPathRefs[(*straightPathCount)-1] = ref;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Append new vertex.
							 | 
						||
| 
								 | 
							
										dtVcopy(&straightPath[(*straightPathCount)*3], pos);
							 | 
						||
| 
								 | 
							
										if (straightPathFlags)
							 | 
						||
| 
								 | 
							
											straightPathFlags[(*straightPathCount)] = flags;
							 | 
						||
| 
								 | 
							
										if (straightPathRefs)
							 | 
						||
| 
								 | 
							
											straightPathRefs[(*straightPathCount)] = ref;
							 | 
						||
| 
								 | 
							
										(*straightPathCount)++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// If there is no space to append more vertices, return.
							 | 
						||
| 
								 | 
							
										if ((*straightPathCount) >= maxStraightPath)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// If reached end of path, return.
							 | 
						||
| 
								 | 
							
										if (flags == DT_STRAIGHTPATH_END)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return DT_IN_PROGRESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
							 | 
						||
| 
								 | 
							
																	  float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
							 | 
						||
| 
								 | 
							
																	  int* straightPathCount, const int maxStraightPath, const int options) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									const float* startPos = &straightPath[(*straightPathCount-1)*3];
							 | 
						||
| 
								 | 
							
									// Append or update last vertex
							 | 
						||
| 
								 | 
							
									dtStatus stat = 0;
							 | 
						||
| 
								 | 
							
									for (int i = startIdx; i < endIdx; i++)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Calculate portal
							 | 
						||
| 
								 | 
							
										const dtPolyRef from = path[i];
							 | 
						||
| 
								 | 
							
										const dtMeshTile* fromTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* fromPoly = 0;
							 | 
						||
| 
								 | 
							
										if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly)))
							 | 
						||
| 
								 | 
							
											return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										const dtPolyRef to = path[i+1];
							 | 
						||
| 
								 | 
							
										const dtMeshTile* toTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* toPoly = 0;
							 | 
						||
| 
								 | 
							
										if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly)))
							 | 
						||
| 
								 | 
							
											return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										float left[3], right[3];
							 | 
						||
| 
								 | 
							
										if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right)))
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
										if (options & DT_STRAIGHTPATH_AREA_CROSSINGS)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Skip intersection if only area crossings are requested.
							 | 
						||
| 
								 | 
							
											if (fromPoly->getArea() == toPoly->getArea())
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Append intersection
							 | 
						||
| 
								 | 
							
										float s,t;
							 | 
						||
| 
								 | 
							
										if (dtIntersectSegSeg2D(startPos, endPos, left, right, s, t))
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											float pt[3];
							 | 
						||
| 
								 | 
							
											dtVlerp(pt, left,right, t);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											stat = appendVertex(pt, 0, path[i+1],
							 | 
						||
| 
								 | 
							
																straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
											if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
												return stat;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return DT_IN_PROGRESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// This method peforms what is often called 'string pulling'.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The start position is clamped to the first polygon in the path, and the 
							 | 
						||
| 
								 | 
							
								/// end position is clamped to the last. So the start and end positions should 
							 | 
						||
| 
								 | 
							
								/// normally be within or very near the first and last polygons respectively.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The returned polygon references represent the reference id of the polygon 
							 | 
						||
| 
								 | 
							
								/// that is entered at the associated path position. The reference id associated 
							 | 
						||
| 
								 | 
							
								/// with the end point will always be zero.  This allows, for example, matching 
							 | 
						||
| 
								 | 
							
								/// off-mesh link points to their representative polygons.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the provided result buffers are too small for the entire result set, 
							 | 
						||
| 
								 | 
							
								/// they will be filled as far as possible from the start toward the end 
							 | 
						||
| 
								 | 
							
								/// position.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																		  const dtPolyRef* path, const int pathSize,
							 | 
						||
| 
								 | 
							
																		  float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
							 | 
						||
| 
								 | 
							
																		  int* straightPathCount, const int maxStraightPath, const int options) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!straightPathCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*straightPathCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!startPos || !dtVisfinite(startPos) ||
							 | 
						||
| 
								 | 
							
										!endPos || !dtVisfinite(endPos) ||
							 | 
						||
| 
								 | 
							
										!path || pathSize <= 0 || !path[0] ||
							 | 
						||
| 
								 | 
							
										maxStraightPath <= 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus stat = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// TODO: Should this be callers responsibility?
							 | 
						||
| 
								 | 
							
									float closestStartPos[3];
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(closestPointOnPolyBoundary(path[0], startPos, closestStartPos)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									float closestEndPos[3];
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Add start point.
							 | 
						||
| 
								 | 
							
									stat = appendVertex(closestStartPos, DT_STRAIGHTPATH_START, path[0],
							 | 
						||
| 
								 | 
							
														straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
														straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
									if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
										return stat;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (pathSize > 1)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										float portalApex[3], portalLeft[3], portalRight[3];
							 | 
						||
| 
								 | 
							
										dtVcopy(portalApex, closestStartPos);
							 | 
						||
| 
								 | 
							
										dtVcopy(portalLeft, portalApex);
							 | 
						||
| 
								 | 
							
										dtVcopy(portalRight, portalApex);
							 | 
						||
| 
								 | 
							
										int apexIndex = 0;
							 | 
						||
| 
								 | 
							
										int leftIndex = 0;
							 | 
						||
| 
								 | 
							
										int rightIndex = 0;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										unsigned char leftPolyType = 0;
							 | 
						||
| 
								 | 
							
										unsigned char rightPolyType = 0;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										dtPolyRef leftPolyRef = path[0];
							 | 
						||
| 
								 | 
							
										dtPolyRef rightPolyRef = path[0];
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < pathSize; ++i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											float left[3], right[3];
							 | 
						||
| 
								 | 
							
											unsigned char toType;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (i+1 < pathSize)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												unsigned char fromType; // fromType is ignored.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												// Next portal.
							 | 
						||
| 
								 | 
							
												if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													// Failed to get portal points, in practice this means that path[i+1] is invalid polygon.
							 | 
						||
| 
								 | 
							
													// Clamp the end point to path[i], and return the path so far.
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													if (dtStatusFailed(closestPointOnPolyBoundary(path[i], endPos, closestEndPos)))
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														// This should only happen when the first polygon is invalid.
							 | 
						||
| 
								 | 
							
														return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Apeend portals along the current straight path segment.
							 | 
						||
| 
								 | 
							
													if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														// Ignore status return value as we're just about to return anyway.
							 | 
						||
| 
								 | 
							
														appendPortals(apexIndex, i, closestEndPos, path,
							 | 
						||
| 
								 | 
							
																			 straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																			 straightPathCount, maxStraightPath, options);
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Ignore status return value as we're just about to return anyway.
							 | 
						||
| 
								 | 
							
													appendVertex(closestEndPos, 0, path[i],
							 | 
						||
| 
								 | 
							
																		straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																		straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													return DT_SUCCESS | DT_PARTIAL_RESULT | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// If starting really close the portal, advance.
							 | 
						||
| 
								 | 
							
												if (i == 0)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													float t;
							 | 
						||
| 
								 | 
							
													if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f))
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// End of the path.
							 | 
						||
| 
								 | 
							
												dtVcopy(left, closestEndPos);
							 | 
						||
| 
								 | 
							
												dtVcopy(right, closestEndPos);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												toType = DT_POLYTYPE_GROUND;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Right vertex.
							 | 
						||
| 
								 | 
							
											if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													dtVcopy(portalRight, right);
							 | 
						||
| 
								 | 
							
													rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
							 | 
						||
| 
								 | 
							
													rightPolyType = toType;
							 | 
						||
| 
								 | 
							
													rightIndex = i;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													// Append portals along the current straight path segment.
							 | 
						||
| 
								 | 
							
													if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														stat = appendPortals(apexIndex, leftIndex, portalLeft, path,
							 | 
						||
| 
								 | 
							
																			 straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																			 straightPathCount, maxStraightPath, options);
							 | 
						||
| 
								 | 
							
														if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
															return stat;					
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
													dtVcopy(portalApex, portalLeft);
							 | 
						||
| 
								 | 
							
													apexIndex = leftIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													unsigned char flags = 0;
							 | 
						||
| 
								 | 
							
													if (!leftPolyRef)
							 | 
						||
| 
								 | 
							
														flags = DT_STRAIGHTPATH_END;
							 | 
						||
| 
								 | 
							
													else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
														flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
							 | 
						||
| 
								 | 
							
													dtPolyRef ref = leftPolyRef;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													// Append or update vertex
							 | 
						||
| 
								 | 
							
													stat = appendVertex(portalApex, flags, ref,
							 | 
						||
| 
								 | 
							
																		straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																		straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
													if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
														return stat;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													dtVcopy(portalLeft, portalApex);
							 | 
						||
| 
								 | 
							
													dtVcopy(portalRight, portalApex);
							 | 
						||
| 
								 | 
							
													leftIndex = apexIndex;
							 | 
						||
| 
								 | 
							
													rightIndex = apexIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													// Restart
							 | 
						||
| 
								 | 
							
													i = apexIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Left vertex.
							 | 
						||
| 
								 | 
							
											if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													dtVcopy(portalLeft, left);
							 | 
						||
| 
								 | 
							
													leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
							 | 
						||
| 
								 | 
							
													leftPolyType = toType;
							 | 
						||
| 
								 | 
							
													leftIndex = i;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													// Append portals along the current straight path segment.
							 | 
						||
| 
								 | 
							
													if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														stat = appendPortals(apexIndex, rightIndex, portalRight, path,
							 | 
						||
| 
								 | 
							
																			 straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																			 straightPathCount, maxStraightPath, options);
							 | 
						||
| 
								 | 
							
														if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
															return stat;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													dtVcopy(portalApex, portalRight);
							 | 
						||
| 
								 | 
							
													apexIndex = rightIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													unsigned char flags = 0;
							 | 
						||
| 
								 | 
							
													if (!rightPolyRef)
							 | 
						||
| 
								 | 
							
														flags = DT_STRAIGHTPATH_END;
							 | 
						||
| 
								 | 
							
													else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
														flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
							 | 
						||
| 
								 | 
							
													dtPolyRef ref = rightPolyRef;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Append or update vertex
							 | 
						||
| 
								 | 
							
													stat = appendVertex(portalApex, flags, ref,
							 | 
						||
| 
								 | 
							
																		straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																		straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
													if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
														return stat;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													dtVcopy(portalLeft, portalApex);
							 | 
						||
| 
								 | 
							
													dtVcopy(portalRight, portalApex);
							 | 
						||
| 
								 | 
							
													leftIndex = apexIndex;
							 | 
						||
| 
								 | 
							
													rightIndex = apexIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													// Restart
							 | 
						||
| 
								 | 
							
													i = apexIndex;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Append portals along the current straight path segment.
							 | 
						||
| 
								 | 
							
										if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											stat = appendPortals(apexIndex, pathSize-1, closestEndPos, path,
							 | 
						||
| 
								 | 
							
																 straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
																 straightPathCount, maxStraightPath, options);
							 | 
						||
| 
								 | 
							
											if (stat != DT_IN_PROGRESS)
							 | 
						||
| 
								 | 
							
												return stat;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Ignore status return value as we're just about to return anyway.
							 | 
						||
| 
								 | 
							
									appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,
							 | 
						||
| 
								 | 
							
														straightPath, straightPathFlags, straightPathRefs,
							 | 
						||
| 
								 | 
							
														straightPathCount, maxStraightPath);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This method is optimized for small delta movement and a small number of 
							 | 
						||
| 
								 | 
							
								/// polygons. If used for too great a distance, the result set will form an 
							 | 
						||
| 
								 | 
							
								/// incomplete path.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @p resultPos will equal the @p endPos if the end is reached. 
							 | 
						||
| 
								 | 
							
								/// Otherwise the closest reachable position will be returned.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// @p resultPos is not projected onto the surface of the navigation 
							 | 
						||
| 
								 | 
							
								/// mesh. Use #getPolyHeight if this is needed.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This method treats the end position in the same manner as 
							 | 
						||
| 
								 | 
							
								/// the #raycast method. (As a 2D point.) See that method's documentation 
							 | 
						||
| 
								 | 
							
								/// for details.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If the @p visited array is too small to hold the entire result set, it will 
							 | 
						||
| 
								 | 
							
								/// be filled as far as possible from the start position toward the end 
							 | 
						||
| 
								 | 
							
								/// position.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																		  const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																		  float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_tinyNodePool);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!visitedCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*visitedCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!startPos || !dtVisfinite(startPos) ||
							 | 
						||
| 
								 | 
							
										!endPos || !dtVisfinite(endPos) ||
							 | 
						||
| 
								 | 
							
										!filter || !resultPos || !visited ||
							 | 
						||
| 
								 | 
							
										maxVisitedSize <= 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									static const int MAX_STACK = 48;
							 | 
						||
| 
								 | 
							
									dtNode* stack[MAX_STACK];
							 | 
						||
| 
								 | 
							
									int nstack = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_tinyNodePool->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_tinyNodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
									stack[nstack++] = startNode;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float bestPos[3];
							 | 
						||
| 
								 | 
							
									float bestDist = FLT_MAX;
							 | 
						||
| 
								 | 
							
									dtNode* bestNode = 0;
							 | 
						||
| 
								 | 
							
									dtVcopy(bestPos, startPos);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Search constraints
							 | 
						||
| 
								 | 
							
									float searchPos[3], searchRadSqr;
							 | 
						||
| 
								 | 
							
									dtVlerp(searchPos, startPos, endPos, 0.5f);
							 | 
						||
| 
								 | 
							
									searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float verts[DT_VERTS_PER_POLYGON*3];
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (nstack)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Pop front.
							 | 
						||
| 
								 | 
							
										dtNode* curNode = stack[0];
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < nstack-1; ++i)
							 | 
						||
| 
								 | 
							
											stack[i] = stack[i+1];
							 | 
						||
| 
								 | 
							
										nstack--;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef curRef = curNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* curTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* curPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);			
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Collect vertices.
							 | 
						||
| 
								 | 
							
										const int nverts = curPoly->vertCount;
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < nverts; ++i)
							 | 
						||
| 
								 | 
							
											dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// If target is inside the poly, stop search.
							 | 
						||
| 
								 | 
							
										if (dtPointInPolygon(endPos, verts, nverts))
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											bestNode = curNode;
							 | 
						||
| 
								 | 
							
											dtVcopy(bestPos, endPos);
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Find wall edges and find nearest point inside the walls.
							 | 
						||
| 
								 | 
							
										for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Find links to neighbours.
							 | 
						||
| 
								 | 
							
											static const int MAX_NEIS = 8;
							 | 
						||
| 
								 | 
							
											int nneis = 0;
							 | 
						||
| 
								 | 
							
											dtPolyRef neis[MAX_NEIS];
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (curPoly->neis[j] & DT_EXT_LINK)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Tile border.
							 | 
						||
| 
								 | 
							
												for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													const dtLink* link = &curTile->links[k];
							 | 
						||
| 
								 | 
							
													if (link->edge == j)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														if (link->ref != 0)
							 | 
						||
| 
								 | 
							
														{
							 | 
						||
| 
								 | 
							
															const dtMeshTile* neiTile = 0;
							 | 
						||
| 
								 | 
							
															const dtPoly* neiPoly = 0;
							 | 
						||
| 
								 | 
							
															m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
							 | 
						||
| 
								 | 
							
															if (filter->passFilter(link->ref, neiTile, neiPoly))
							 | 
						||
| 
								 | 
							
															{
							 | 
						||
| 
								 | 
							
																if (nneis < MAX_NEIS)
							 | 
						||
| 
								 | 
							
																	neis[nneis++] = link->ref;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else if (curPoly->neis[j])
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const unsigned int idx = (unsigned int)(curPoly->neis[j]-1);
							 | 
						||
| 
								 | 
							
												const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx;
							 | 
						||
| 
								 | 
							
												if (filter->passFilter(ref, curTile, &curTile->polys[idx]))
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													// Internal edge, encode id.
							 | 
						||
| 
								 | 
							
													neis[nneis++] = ref;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (!nneis)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Wall edge, calc distance.
							 | 
						||
| 
								 | 
							
												const float* vj = &verts[j*3];
							 | 
						||
| 
								 | 
							
												const float* vi = &verts[i*3];
							 | 
						||
| 
								 | 
							
												float tseg;
							 | 
						||
| 
								 | 
							
												const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg);
							 | 
						||
| 
								 | 
							
												if (distSqr < bestDist)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
								                    // Update nearest distance.
							 | 
						||
| 
								 | 
							
													dtVlerp(bestPos, vj,vi, tseg);
							 | 
						||
| 
								 | 
							
													bestDist = distSqr;
							 | 
						||
| 
								 | 
							
													bestNode = curNode;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												for (int k = 0; k < nneis; ++k)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													// Skip if no node can be allocated.
							 | 
						||
| 
								 | 
							
													dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]);
							 | 
						||
| 
								 | 
							
													if (!neighbourNode)
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
													// Skip if already visited.
							 | 
						||
| 
								 | 
							
													if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													// Skip the link if it is too far from search constraint.
							 | 
						||
| 
								 | 
							
													// TODO: Maybe should use getPortalPoints(), but this one is way faster.
							 | 
						||
| 
								 | 
							
													const float* vj = &verts[j*3];
							 | 
						||
| 
								 | 
							
													const float* vi = &verts[i*3];
							 | 
						||
| 
								 | 
							
													float tseg;
							 | 
						||
| 
								 | 
							
													float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg);
							 | 
						||
| 
								 | 
							
													if (distSqr > searchRadSqr)
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													// Mark as the node as visited and push to queue.
							 | 
						||
| 
								 | 
							
													if (nstack < MAX_STACK)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
							 | 
						||
| 
								 | 
							
														neighbourNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
														stack[nstack++] = neighbourNode;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									if (bestNode)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Reverse the path.
							 | 
						||
| 
								 | 
							
										dtNode* prev = 0;
							 | 
						||
| 
								 | 
							
										dtNode* node = bestNode;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
											node->pidx = m_tinyNodePool->getNodeIdx(prev);
							 | 
						||
| 
								 | 
							
											prev = node;
							 | 
						||
| 
								 | 
							
											node = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Store result
							 | 
						||
| 
								 | 
							
										node = prev;
							 | 
						||
| 
								 | 
							
										do
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											visited[n++] = node->id;
							 | 
						||
| 
								 | 
							
											if (n >= maxVisitedSize)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											node = m_tinyNodePool->getNodeAtIdx(node->pidx);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										while (node);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtVcopy(resultPos, bestPos);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*visitedCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
							 | 
						||
| 
								 | 
							
																		 unsigned char& fromType, unsigned char& toType) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const dtMeshTile* fromTile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* fromPoly = 0;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									fromType = fromPoly->getType();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const dtMeshTile* toTile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* toPoly = 0;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									toType = toPoly->getType();
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
									return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Returns portal points between two polygons.
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
							 | 
						||
| 
								 | 
							
																		 dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
							 | 
						||
| 
								 | 
							
																		 float* left, float* right) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// Find the link that points to the 'to' polygon.
							 | 
						||
| 
								 | 
							
									const dtLink* link = 0;
							 | 
						||
| 
								 | 
							
									for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										if (fromTile->links[i].ref == to)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											link = &fromTile->links[i];
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if (!link)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Handle off-mesh connections.
							 | 
						||
| 
								 | 
							
									if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Find link that points to first vertex.
							 | 
						||
| 
								 | 
							
										for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if (fromTile->links[i].ref == to)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const int v = fromTile->links[i].edge;
							 | 
						||
| 
								 | 
							
												dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]);
							 | 
						||
| 
								 | 
							
												dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]);
							 | 
						||
| 
								 | 
							
												return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if (toTile->links[i].ref == from)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const int v = toTile->links[i].edge;
							 | 
						||
| 
								 | 
							
												dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]);
							 | 
						||
| 
								 | 
							
												dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]);
							 | 
						||
| 
								 | 
							
												return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Find portal vertices.
							 | 
						||
| 
								 | 
							
									const int v0 = fromPoly->verts[link->edge];
							 | 
						||
| 
								 | 
							
									const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount];
							 | 
						||
| 
								 | 
							
									dtVcopy(left, &fromTile->verts[v0*3]);
							 | 
						||
| 
								 | 
							
									dtVcopy(right, &fromTile->verts[v1*3]);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// If the link is at tile boundary, dtClamp the vertices to
							 | 
						||
| 
								 | 
							
									// the link width.
							 | 
						||
| 
								 | 
							
									if (link->side != 0xff)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Unpack portal limits.
							 | 
						||
| 
								 | 
							
										if (link->bmin != 0 || link->bmax != 255)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const float s = 1.0f/255.0f;
							 | 
						||
| 
								 | 
							
											const float tmin = link->bmin*s;
							 | 
						||
| 
								 | 
							
											const float tmax = link->bmax*s;
							 | 
						||
| 
								 | 
							
											dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin);
							 | 
						||
| 
								 | 
							
											dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Returns edge mid point between two polygons.
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									float left[3], right[3];
							 | 
						||
| 
								 | 
							
									unsigned char fromType, toType;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(getPortalPoints(from, to, left,right, fromType, toType)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									mid[0] = (left[0]+right[0])*0.5f;
							 | 
						||
| 
								 | 
							
									mid[1] = (left[1]+right[1])*0.5f;
							 | 
						||
| 
								 | 
							
									mid[2] = (left[2]+right[2])*0.5f;
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
							 | 
						||
| 
								 | 
							
																		 dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
							 | 
						||
| 
								 | 
							
																		 float* mid) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									float left[3], right[3];
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									mid[0] = (left[0]+right[0])*0.5f;
							 | 
						||
| 
								 | 
							
									mid[1] = (left[1]+right[1])*0.5f;
							 | 
						||
| 
								 | 
							
									mid[2] = (left[2]+right[2])*0.5f;
							 | 
						||
| 
								 | 
							
									return DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This method is meant to be used for quick, short distance checks.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the path array is too small to hold the result, it will be filled as 
							 | 
						||
| 
								 | 
							
								/// far as possible from the start postion toward the end position.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>Using the Hit Parameter (t)</b>
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit 
							 | 
						||
| 
								 | 
							
								/// the end position. In this case the path represents a valid corridor to the 
							 | 
						||
| 
								 | 
							
								/// end position and the value of @p hitNormal is undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the hit parameter is zero, then the start position is on the wall that 
							 | 
						||
| 
								 | 
							
								/// was hit and the value of @p hitNormal is undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If 0 < t < 1.0 then the following applies:
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @code
							 | 
						||
| 
								 | 
							
								/// distanceToHitBorder = distanceToEndPosition * t
							 | 
						||
| 
								 | 
							
								/// hitPoint = startPos + (endPos - startPos) * t
							 | 
						||
| 
								 | 
							
								/// @endcode
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>Use Case Restriction</b>
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The raycast ignores the y-value of the end position. (2D check.) This 
							 | 
						||
| 
								 | 
							
								/// places significant limits on how it can be used. For example:
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Consider a scene where there is a main floor with a second floor balcony 
							 | 
						||
| 
								 | 
							
								/// that hangs over the main floor. So the first floor mesh extends below the 
							 | 
						||
| 
								 | 
							
								/// balcony mesh. The start position is somewhere on the first floor. The end 
							 | 
						||
| 
								 | 
							
								/// position is on the balcony.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The raycast will search toward the end position along the first floor mesh. 
							 | 
						||
| 
								 | 
							
								/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
							 | 
						||
| 
								 | 
							
								/// (no wall hit), meaning it reached the end position. This is one example of why
							 | 
						||
| 
								 | 
							
								/// this method is meant for short distance checks.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																 const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																 float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtRaycastHit hit;
							 | 
						||
| 
								 | 
							
									hit.path = path;
							 | 
						||
| 
								 | 
							
									hit.maxPath = maxPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*t = hit.t;
							 | 
						||
| 
								 | 
							
									if (hitNormal)
							 | 
						||
| 
								 | 
							
										dtVcopy(hitNormal, hit.hitNormal);
							 | 
						||
| 
								 | 
							
									if (pathCount)
							 | 
						||
| 
								 | 
							
										*pathCount = hit.pathCount;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This method is meant to be used for quick, short distance checks.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the path array is too small to hold the result, it will be filled as 
							 | 
						||
| 
								 | 
							
								/// far as possible from the start postion toward the end position.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>Using the Hit Parameter t of RaycastHit</b>
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit 
							 | 
						||
| 
								 | 
							
								/// the end position. In this case the path represents a valid corridor to the 
							 | 
						||
| 
								 | 
							
								/// end position and the value of @p hitNormal is undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the hit parameter is zero, then the start position is on the wall that 
							 | 
						||
| 
								 | 
							
								/// was hit and the value of @p hitNormal is undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If 0 < t < 1.0 then the following applies:
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @code
							 | 
						||
| 
								 | 
							
								/// distanceToHitBorder = distanceToEndPosition * t
							 | 
						||
| 
								 | 
							
								/// hitPoint = startPos + (endPos - startPos) * t
							 | 
						||
| 
								 | 
							
								/// @endcode
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// <b>Use Case Restriction</b>
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The raycast ignores the y-value of the end position. (2D check.) This 
							 | 
						||
| 
								 | 
							
								/// places significant limits on how it can be used. For example:
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Consider a scene where there is a main floor with a second floor balcony 
							 | 
						||
| 
								 | 
							
								/// that hangs over the main floor. So the first floor mesh extends below the 
							 | 
						||
| 
								 | 
							
								/// balcony mesh. The start position is somewhere on the first floor. The end 
							 | 
						||
| 
								 | 
							
								/// position is on the balcony.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The raycast will search toward the end position along the first floor mesh. 
							 | 
						||
| 
								 | 
							
								/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
							 | 
						||
| 
								 | 
							
								/// (no wall hit), meaning it reached the end position. This is one example of why
							 | 
						||
| 
								 | 
							
								/// this method is meant for short distance checks.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
							 | 
						||
| 
								 | 
							
																 const dtQueryFilter* filter, const unsigned int options,
							 | 
						||
| 
								 | 
							
																 dtRaycastHit* hit, dtPolyRef prevRef) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!hit)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									hit->t = 0;
							 | 
						||
| 
								 | 
							
									hit->pathCount = 0;
							 | 
						||
| 
								 | 
							
									hit->pathCost = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!startPos || !dtVisfinite(startPos) ||
							 | 
						||
| 
								 | 
							
										!endPos || !dtVisfinite(endPos) ||
							 | 
						||
| 
								 | 
							
										!filter ||
							 | 
						||
| 
								 | 
							
										(prevRef && !m_nav->isValidPolyRef(prevRef)))
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float dir[3], curPos[3], lastPos[3];
							 | 
						||
| 
								 | 
							
									float verts[DT_VERTS_PER_POLYGON*3+3];	
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtVcopy(curPos, startPos);
							 | 
						||
| 
								 | 
							
									dtVsub(dir, endPos, startPos);
							 | 
						||
| 
								 | 
							
									dtVset(hit->hitNormal, 0, 0, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const dtMeshTile* prevTile, *tile, *nextTile;
							 | 
						||
| 
								 | 
							
									const dtPoly* prevPoly, *poly, *nextPoly;
							 | 
						||
| 
								 | 
							
									dtPolyRef curRef;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// The API input has been checked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
									curRef = startRef;
							 | 
						||
| 
								 | 
							
									tile = 0;
							 | 
						||
| 
								 | 
							
									poly = 0;
							 | 
						||
| 
								 | 
							
									m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
							 | 
						||
| 
								 | 
							
									nextTile = prevTile = tile;
							 | 
						||
| 
								 | 
							
									nextPoly = prevPoly = poly;
							 | 
						||
| 
								 | 
							
									if (prevRef)
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									while (curRef)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Cast ray against current polygon.
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Collect vertices.
							 | 
						||
| 
								 | 
							
										int nv = 0;
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < (int)poly->vertCount; ++i)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
							 | 
						||
| 
								 | 
							
											nv++;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										float tmin, tmax;
							 | 
						||
| 
								 | 
							
										int segMin, segMax;
							 | 
						||
| 
								 | 
							
										if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Could not hit the polygon, keep the old t and report hit.
							 | 
						||
| 
								 | 
							
											hit->pathCount = n;
							 | 
						||
| 
								 | 
							
											return status;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										hit->hitEdgeIndex = segMax;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Keep track of furthest t so far.
							 | 
						||
| 
								 | 
							
										if (tmax > hit->t)
							 | 
						||
| 
								 | 
							
											hit->t = tmax;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Store visited polygons.
							 | 
						||
| 
								 | 
							
										if (n < hit->maxPath)
							 | 
						||
| 
								 | 
							
											hit->path[n++] = curRef;
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
											status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Ray end is completely inside the polygon.
							 | 
						||
| 
								 | 
							
										if (segMax == -1)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											hit->t = FLT_MAX;
							 | 
						||
| 
								 | 
							
											hit->pathCount = n;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// add the cost
							 | 
						||
| 
								 | 
							
											if (options & DT_RAYCAST_USE_COSTS)
							 | 
						||
| 
								 | 
							
												hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly);
							 | 
						||
| 
								 | 
							
											return status;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Follow neighbours.
							 | 
						||
| 
								 | 
							
										dtPolyRef nextRef = 0;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &tile->links[i];
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Find link which contains this edge.
							 | 
						||
| 
								 | 
							
											if ((int)link->edge != segMax)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Get pointer to the next polygon.
							 | 
						||
| 
								 | 
							
											nextTile = 0;
							 | 
						||
| 
								 | 
							
											nextPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip off-mesh connections.
							 | 
						||
| 
								 | 
							
											if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip links based on filter.
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(link->ref, nextTile, nextPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the link is internal, just return the ref.
							 | 
						||
| 
								 | 
							
											if (link->side == 0xff)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												nextRef = link->ref;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the link is at tile boundary,
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Check if the link spans the whole edge, and accept.
							 | 
						||
| 
								 | 
							
											if (link->bmin == 0 && link->bmax == 255)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												nextRef = link->ref;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Check for partial edge links.
							 | 
						||
| 
								 | 
							
											const int v0 = poly->verts[link->edge];
							 | 
						||
| 
								 | 
							
											const int v1 = poly->verts[(link->edge+1) % poly->vertCount];
							 | 
						||
| 
								 | 
							
											const float* left = &tile->verts[v0*3];
							 | 
						||
| 
								 | 
							
											const float* right = &tile->verts[v1*3];
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Check that the intersection lies inside the link portal.
							 | 
						||
| 
								 | 
							
											if (link->side == 0 || link->side == 4)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Calculate link size.
							 | 
						||
| 
								 | 
							
												const float s = 1.0f/255.0f;
							 | 
						||
| 
								 | 
							
												float lmin = left[2] + (right[2] - left[2])*(link->bmin*s);
							 | 
						||
| 
								 | 
							
												float lmax = left[2] + (right[2] - left[2])*(link->bmax*s);
							 | 
						||
| 
								 | 
							
												if (lmin > lmax) dtSwap(lmin, lmax);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Find Z intersection.
							 | 
						||
| 
								 | 
							
												float z = startPos[2] + (endPos[2]-startPos[2])*tmax;
							 | 
						||
| 
								 | 
							
												if (z >= lmin && z <= lmax)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													nextRef = link->ref;
							 | 
						||
| 
								 | 
							
													break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else if (link->side == 2 || link->side == 6)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Calculate link size.
							 | 
						||
| 
								 | 
							
												const float s = 1.0f/255.0f;
							 | 
						||
| 
								 | 
							
												float lmin = left[0] + (right[0] - left[0])*(link->bmin*s);
							 | 
						||
| 
								 | 
							
												float lmax = left[0] + (right[0] - left[0])*(link->bmax*s);
							 | 
						||
| 
								 | 
							
												if (lmin > lmax) dtSwap(lmin, lmax);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Find X intersection.
							 | 
						||
| 
								 | 
							
												float x = startPos[0] + (endPos[0]-startPos[0])*tmax;
							 | 
						||
| 
								 | 
							
												if (x >= lmin && x <= lmax)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													nextRef = link->ref;
							 | 
						||
| 
								 | 
							
													break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// add the cost
							 | 
						||
| 
								 | 
							
										if (options & DT_RAYCAST_USE_COSTS)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// compute the intersection point at the furthest end of the polygon
							 | 
						||
| 
								 | 
							
											// and correct the height (since the raycast moves in 2d)
							 | 
						||
| 
								 | 
							
											dtVcopy(lastPos, curPos);
							 | 
						||
| 
								 | 
							
											dtVmad(curPos, startPos, dir, hit->t);
							 | 
						||
| 
								 | 
							
											float* e1 = &verts[segMax*3];
							 | 
						||
| 
								 | 
							
											float* e2 = &verts[((segMax+1)%nv)*3];
							 | 
						||
| 
								 | 
							
											float eDir[3], diff[3];
							 | 
						||
| 
								 | 
							
											dtVsub(eDir, e2, e1);
							 | 
						||
| 
								 | 
							
											dtVsub(diff, curPos, e1);
							 | 
						||
| 
								 | 
							
											float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2];
							 | 
						||
| 
								 | 
							
											curPos[1] = e1[1] + eDir[1] * s;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!nextRef)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// No neighbour, we hit a wall.
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Calculate hit normal.
							 | 
						||
| 
								 | 
							
											const int a = segMax;
							 | 
						||
| 
								 | 
							
											const int b = segMax+1 < nv ? segMax+1 : 0;
							 | 
						||
| 
								 | 
							
											const float* va = &verts[a*3];
							 | 
						||
| 
								 | 
							
											const float* vb = &verts[b*3];
							 | 
						||
| 
								 | 
							
											const float dx = vb[0] - va[0];
							 | 
						||
| 
								 | 
							
											const float dz = vb[2] - va[2];
							 | 
						||
| 
								 | 
							
											hit->hitNormal[0] = dz;
							 | 
						||
| 
								 | 
							
											hit->hitNormal[1] = 0;
							 | 
						||
| 
								 | 
							
											hit->hitNormal[2] = -dx;
							 | 
						||
| 
								 | 
							
											dtVnormalize(hit->hitNormal);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											hit->pathCount = n;
							 | 
						||
| 
								 | 
							
											return status;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// No hit, advance to neighbour polygon.
							 | 
						||
| 
								 | 
							
										prevRef = curRef;
							 | 
						||
| 
								 | 
							
										curRef = nextRef;
							 | 
						||
| 
								 | 
							
										prevTile = tile;
							 | 
						||
| 
								 | 
							
										tile = nextTile;
							 | 
						||
| 
								 | 
							
										prevPoly = poly;
							 | 
						||
| 
								 | 
							
										poly = nextPoly;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									hit->pathCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// At least one result array must be provided.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The order of the result set is from least to highest cost to reach the polygon.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// A common use case for this method is to perform Dijkstra searches. 
							 | 
						||
| 
								 | 
							
								/// Candidate polygons are found by searching the graph beginning at the start polygon.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If a polygon is not found via the graph search, even if it intersects the 
							 | 
						||
| 
								 | 
							
								/// search circle, it will not be included in the result set. For example:
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// polyA is the start polygon.
							 | 
						||
| 
								 | 
							
								/// polyB shares an edge with polyA. (Is adjacent.)
							 | 
						||
| 
								 | 
							
								/// polyC shares an edge with polyB, but not with polyA
							 | 
						||
| 
								 | 
							
								/// Even if the search circle overlaps polyC, it will not be included in the 
							 | 
						||
| 
								 | 
							
								/// result set unless polyB is also in the set.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// The value of the center point is used as the start position for cost 
							 | 
						||
| 
								 | 
							
								/// calculations. It is not projected onto the surface of the mesh, so its 
							 | 
						||
| 
								 | 
							
								/// y-value will effect the costs.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Intersection tests occur in 2D. All polygons and the search circle are 
							 | 
						||
| 
								 | 
							
								/// projected onto the xz-plane. So the y-value of the center point does not 
							 | 
						||
| 
								 | 
							
								/// effect intersection tests.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the result arrays are to small to hold the entire result set, they will be 
							 | 
						||
| 
								 | 
							
								/// filled to capacity.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
							 | 
						||
| 
								 | 
							
																			   const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																			   dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
							 | 
						||
| 
								 | 
							
																			   int* resultCount, const int maxResult) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!resultCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*resultCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!centerPos || !dtVisfinite(centerPos) ||
							 | 
						||
| 
								 | 
							
										radius < 0 || !dtMathIsfinite(radius) ||
							 | 
						||
| 
								 | 
							
										!filter || maxResult < 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, centerPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const float radiusSqr = dtSqr(radius);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (!m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
											parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (n < maxResult)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if (resultRef)
							 | 
						||
| 
								 | 
							
												resultRef[n] = bestRef;
							 | 
						||
| 
								 | 
							
											if (resultParent)
							 | 
						||
| 
								 | 
							
												resultParent[n] = parentRef;
							 | 
						||
| 
								 | 
							
											if (resultCost)
							 | 
						||
| 
								 | 
							
												resultCost[n] = bestNode->total;
							 | 
						||
| 
								 | 
							
											++n;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &bestTile->links[i];
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = link->ref;
							 | 
						||
| 
								 | 
							
											// Skip invalid neighbours and do not follow back to parent.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Expand to neighbour
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
											// Do not advance if the polygon is excluded by the filter.
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Find edge and calc distance to the edge.
							 | 
						||
| 
								 | 
							
											float va[3], vb[3];
							 | 
						||
| 
								 | 
							
											if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the circle is not touching the next polygon, skip it.
							 | 
						||
| 
								 | 
							
											float tseg;
							 | 
						||
| 
								 | 
							
											float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
							 | 
						||
| 
								 | 
							
											if (distSqr > radiusSqr)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Cost
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
												dtVlerp(neighbourNode->pos, va, vb, 0.5f);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											float cost = filter->getCost(
							 | 
						||
| 
								 | 
							
												bestNode->pos, neighbourNode->pos,
							 | 
						||
| 
								 | 
							
												parentRef, parentTile, parentPoly,
							 | 
						||
| 
								 | 
							
												bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
												neighbourRef, neighbourTile, neighbourPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const float total = bestNode->total + cost;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												neighbourNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*resultCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The order of the result set is from least to highest cost.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// At least one result array must be provided.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// A common use case for this method is to perform Dijkstra searches. 
							 | 
						||
| 
								 | 
							
								/// Candidate polygons are found by searching the graph beginning at the start 
							 | 
						||
| 
								 | 
							
								/// polygon.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// The same intersection test restrictions that apply to findPolysAroundCircle()
							 | 
						||
| 
								 | 
							
								/// method apply to this method.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// The 3D centroid of the search polygon is used as the start position for cost 
							 | 
						||
| 
								 | 
							
								/// calculations.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// Intersection tests occur in 2D. All polygons are projected onto the 
							 | 
						||
| 
								 | 
							
								/// xz-plane. So the y-values of the vertices do not effect intersection tests.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If the result arrays are is too small to hold the entire result set, they will 
							 | 
						||
| 
								 | 
							
								/// be filled to capacity.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
							 | 
						||
| 
								 | 
							
																			  const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																			  dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
							 | 
						||
| 
								 | 
							
																			  int* resultCount, const int maxResult) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!resultCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*resultCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!verts || nverts < 3 ||
							 | 
						||
| 
								 | 
							
										!filter || maxResult < 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!startRef || !m_nav->isValidPolyRef(startRef))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float centerPos[3] = {0,0,0};
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < nverts; ++i)
							 | 
						||
| 
								 | 
							
										dtVadd(centerPos,centerPos,&verts[i*3]);
							 | 
						||
| 
								 | 
							
									dtVscale(centerPos,centerPos,1.0f/nverts);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, centerPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (!m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
											parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (n < maxResult)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											if (resultRef)
							 | 
						||
| 
								 | 
							
												resultRef[n] = bestRef;
							 | 
						||
| 
								 | 
							
											if (resultParent)
							 | 
						||
| 
								 | 
							
												resultParent[n] = parentRef;
							 | 
						||
| 
								 | 
							
											if (resultCost)
							 | 
						||
| 
								 | 
							
												resultCost[n] = bestNode->total;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											++n;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &bestTile->links[i];
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = link->ref;
							 | 
						||
| 
								 | 
							
											// Skip invalid neighbours and do not follow back to parent.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Expand to neighbour
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Do not advance if the polygon is excluded by the filter.
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Find edge and calc distance to the edge.
							 | 
						||
| 
								 | 
							
											float va[3], vb[3];
							 | 
						||
| 
								 | 
							
											if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the poly is not touching the edge to the next polygon, skip the connection it.
							 | 
						||
| 
								 | 
							
											float tmin, tmax;
							 | 
						||
| 
								 | 
							
											int segMin, segMax;
							 | 
						||
| 
								 | 
							
											if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											if (tmin > 1.0f || tmax < 0.0f)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Cost
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
												dtVlerp(neighbourNode->pos, va, vb, 0.5f);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											float cost = filter->getCost(
							 | 
						||
| 
								 | 
							
												bestNode->pos, neighbourNode->pos,
							 | 
						||
| 
								 | 
							
												parentRef, parentTile, parentPoly,
							 | 
						||
| 
								 | 
							
												bestRef, bestTile, bestPoly,
							 | 
						||
| 
								 | 
							
												neighbourRef, neighbourTile, neighbourPoly);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const float total = bestNode->total + cost;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												neighbourNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*resultCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(endRef) || !path || !pathCount || maxPath < 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*pathCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dtNode* endNode;
							 | 
						||
| 
								 | 
							
									if (m_nodePool->findNodes(endRef, &endNode, 1) != 1 ||
							 | 
						||
| 
								 | 
							
										(endNode->flags & DT_NODE_CLOSED) == 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return getPathToNode(endNode, path, pathCount, maxPath);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// This method is optimized for a small search radius and small number of result 
							 | 
						||
| 
								 | 
							
								/// polygons.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// Candidate polygons are found by searching the navigation graph beginning at 
							 | 
						||
| 
								 | 
							
								/// the start polygon.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The same intersection test restrictions that apply to the findPolysAroundCircle 
							 | 
						||
| 
								 | 
							
								/// mehtod applies to this method.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The value of the center point is used as the start point for cost calculations. 
							 | 
						||
| 
								 | 
							
								/// It is not projected onto the surface of the mesh, so its y-value will effect 
							 | 
						||
| 
								 | 
							
								/// the costs.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// Intersection tests occur in 2D. All polygons and the search circle are 
							 | 
						||
| 
								 | 
							
								/// projected onto the xz-plane. So the y-value of the center point does not 
							 | 
						||
| 
								 | 
							
								/// effect intersection tests.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// If the result arrays are is too small to hold the entire result set, they will 
							 | 
						||
| 
								 | 
							
								/// be filled to capacity.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
							 | 
						||
| 
								 | 
							
																				const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																				dtPolyRef* resultRef, dtPolyRef* resultParent,
							 | 
						||
| 
								 | 
							
																				int* resultCount, const int maxResult) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_tinyNodePool);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!resultCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									*resultCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!centerPos || !dtVisfinite(centerPos) ||
							 | 
						||
| 
								 | 
							
										radius < 0 || !dtMathIsfinite(radius) ||
							 | 
						||
| 
								 | 
							
										!filter || maxResult < 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static const int MAX_STACK = 48;
							 | 
						||
| 
								 | 
							
									dtNode* stack[MAX_STACK];
							 | 
						||
| 
								 | 
							
									int nstack = 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_tinyNodePool->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_tinyNodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
									stack[nstack++] = startNode;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const float radiusSqr = dtSqr(radius);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float pa[DT_VERTS_PER_POLYGON*3];
							 | 
						||
| 
								 | 
							
									float pb[DT_VERTS_PER_POLYGON*3];
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									if (n < maxResult)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										resultRef[n] = startNode->id;
							 | 
						||
| 
								 | 
							
										if (resultParent)
							 | 
						||
| 
								 | 
							
											resultParent[n] = 0;
							 | 
						||
| 
								 | 
							
										++n;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (nstack)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Pop front.
							 | 
						||
| 
								 | 
							
										dtNode* curNode = stack[0];
							 | 
						||
| 
								 | 
							
										for (int i = 0; i < nstack-1; ++i)
							 | 
						||
| 
								 | 
							
											stack[i] = stack[i+1];
							 | 
						||
| 
								 | 
							
										nstack--;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef curRef = curNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* curTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* curPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &curTile->links[i];
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = link->ref;
							 | 
						||
| 
								 | 
							
											// Skip invalid neighbours.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip if cannot alloca more nodes.
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											// Skip visited.
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Expand to neighbour
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip off-mesh connections.
							 | 
						||
| 
								 | 
							
											if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Do not advance if the polygon is excluded by the filter.
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Find edge and calc distance to the edge.
							 | 
						||
| 
								 | 
							
											float va[3], vb[3];
							 | 
						||
| 
								 | 
							
											if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the circle is not touching the next polygon, skip it.
							 | 
						||
| 
								 | 
							
											float tseg;
							 | 
						||
| 
								 | 
							
											float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
							 | 
						||
| 
								 | 
							
											if (distSqr > radiusSqr)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Mark node visited, this is done before the overlap test so that
							 | 
						||
| 
								 | 
							
											// we will not visit the poly again if the test fails.
							 | 
						||
| 
								 | 
							
											neighbourNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Check that the polygon does not collide with existing polygons.
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Collect vertices of the neighbour poly.
							 | 
						||
| 
								 | 
							
											const int npa = neighbourPoly->vertCount;
							 | 
						||
| 
								 | 
							
											for (int k = 0; k < npa; ++k)
							 | 
						||
| 
								 | 
							
												dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											bool overlap = false;
							 | 
						||
| 
								 | 
							
											for (int j = 0; j < n; ++j)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												dtPolyRef pastRef = resultRef[j];
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Connected polys do not overlap.
							 | 
						||
| 
								 | 
							
												bool connected = false;
							 | 
						||
| 
								 | 
							
												for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													if (curTile->links[k].ref == pastRef)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														connected = true;
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												if (connected)
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Potentially overlapping.
							 | 
						||
| 
								 | 
							
												const dtMeshTile* pastTile = 0;
							 | 
						||
| 
								 | 
							
												const dtPoly* pastPoly = 0;
							 | 
						||
| 
								 | 
							
												m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Get vertices and test overlap
							 | 
						||
| 
								 | 
							
												const int npb = pastPoly->vertCount;
							 | 
						||
| 
								 | 
							
												for (int k = 0; k < npb; ++k)
							 | 
						||
| 
								 | 
							
													dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												if (dtOverlapPolyPoly2D(pa,npa, pb,npb))
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													overlap = true;
							 | 
						||
| 
								 | 
							
													break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if (overlap)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// This poly is fine, store and advance to the poly.
							 | 
						||
| 
								 | 
							
											if (n < maxResult)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												resultRef[n] = neighbourRef;
							 | 
						||
| 
								 | 
							
												if (resultParent)
							 | 
						||
| 
								 | 
							
													resultParent[n] = curRef;
							 | 
						||
| 
								 | 
							
												++n;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (nstack < MAX_STACK)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												stack[nstack++] = neighbourNode;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*resultCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct dtSegInterval
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtPolyRef ref;
							 | 
						||
| 
								 | 
							
									short tmin, tmax;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts,
							 | 
						||
| 
								 | 
							
														   const short tmin, const short tmax, const dtPolyRef ref)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (nints+1 > maxInts) return;
							 | 
						||
| 
								 | 
							
									// Find insertion point.
							 | 
						||
| 
								 | 
							
									int idx = 0;
							 | 
						||
| 
								 | 
							
									while (idx < nints)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										if (tmax <= ints[idx].tmin)
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										idx++;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// Move current results.
							 | 
						||
| 
								 | 
							
									if (nints-idx)
							 | 
						||
| 
								 | 
							
										memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx));
							 | 
						||
| 
								 | 
							
									// Store
							 | 
						||
| 
								 | 
							
									ints[idx].ref = ref;
							 | 
						||
| 
								 | 
							
									ints[idx].tmin = tmin;
							 | 
						||
| 
								 | 
							
									ints[idx].tmax = tmax;
							 | 
						||
| 
								 | 
							
									nints++;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// If the @p segmentRefs parameter is provided, then all polygon segments will be returned. 
							 | 
						||
| 
								 | 
							
								/// Otherwise only the wall segments are returned.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// A segment that is normally a portal will be included in the result set as a 
							 | 
						||
| 
								 | 
							
								/// wall if the @p filter results in the neighbor polygon becoomming impassable.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								/// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the 
							 | 
						||
| 
								 | 
							
								/// maximum segments per polygon of the source navigation mesh.
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																			 float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount,
							 | 
						||
| 
								 | 
							
																			 const int maxSegments) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!segmentCount)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*segmentCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const dtMeshTile* tile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* poly = 0;
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!filter || !segmentVerts || maxSegments < 0)
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									int n = 0;
							 | 
						||
| 
								 | 
							
									static const int MAX_INTERVAL = 16;
							 | 
						||
| 
								 | 
							
									dtSegInterval ints[MAX_INTERVAL];
							 | 
						||
| 
								 | 
							
									int nints;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									const bool storePortals = segmentRefs != 0;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// Skip non-solid edges.
							 | 
						||
| 
								 | 
							
										nints = 0;
							 | 
						||
| 
								 | 
							
										if (poly->neis[j] & DT_EXT_LINK)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Tile border.
							 | 
						||
| 
								 | 
							
											for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const dtLink* link = &tile->links[k];
							 | 
						||
| 
								 | 
							
												if (link->edge == j)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													if (link->ref != 0)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														const dtMeshTile* neiTile = 0;
							 | 
						||
| 
								 | 
							
														const dtPoly* neiPoly = 0;
							 | 
						||
| 
								 | 
							
														m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
							 | 
						||
| 
								 | 
							
														if (filter->passFilter(link->ref, neiTile, neiPoly))
							 | 
						||
| 
								 | 
							
														{
							 | 
						||
| 
								 | 
							
															insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax, link->ref);
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Internal edge
							 | 
						||
| 
								 | 
							
											dtPolyRef neiRef = 0;
							 | 
						||
| 
								 | 
							
											if (poly->neis[j])
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const unsigned int idx = (unsigned int)(poly->neis[j]-1);
							 | 
						||
| 
								 | 
							
												neiRef = m_nav->getPolyRefBase(tile) | idx;
							 | 
						||
| 
								 | 
							
												if (!filter->passFilter(neiRef, tile, &tile->polys[idx]))
							 | 
						||
| 
								 | 
							
													neiRef = 0;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// If the edge leads to another polygon and portals are not stored, skip.
							 | 
						||
| 
								 | 
							
											if (neiRef != 0 && !storePortals)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (n < maxSegments)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const float* vj = &tile->verts[poly->verts[j]*3];
							 | 
						||
| 
								 | 
							
												const float* vi = &tile->verts[poly->verts[i]*3];
							 | 
						||
| 
								 | 
							
												float* seg = &segmentVerts[n*6];
							 | 
						||
| 
								 | 
							
												dtVcopy(seg+0, vj);
							 | 
						||
| 
								 | 
							
												dtVcopy(seg+3, vi);
							 | 
						||
| 
								 | 
							
												if (segmentRefs)
							 | 
						||
| 
								 | 
							
													segmentRefs[n] = neiRef;
							 | 
						||
| 
								 | 
							
												n++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											continue;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Add sentinels
							 | 
						||
| 
								 | 
							
										insertInterval(ints, nints, MAX_INTERVAL, -1, 0, 0);
							 | 
						||
| 
								 | 
							
										insertInterval(ints, nints, MAX_INTERVAL, 255, 256, 0);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Store segments.
							 | 
						||
| 
								 | 
							
										const float* vj = &tile->verts[poly->verts[j]*3];
							 | 
						||
| 
								 | 
							
										const float* vi = &tile->verts[poly->verts[i]*3];
							 | 
						||
| 
								 | 
							
										for (int k = 1; k < nints; ++k)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Portal segment.
							 | 
						||
| 
								 | 
							
											if (storePortals && ints[k].ref)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const float tmin = ints[k].tmin/255.0f; 
							 | 
						||
| 
								 | 
							
												const float tmax = ints[k].tmax/255.0f; 
							 | 
						||
| 
								 | 
							
												if (n < maxSegments)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													float* seg = &segmentVerts[n*6];
							 | 
						||
| 
								 | 
							
													dtVlerp(seg+0, vj,vi, tmin);
							 | 
						||
| 
								 | 
							
													dtVlerp(seg+3, vj,vi, tmax);
							 | 
						||
| 
								 | 
							
													if (segmentRefs)
							 | 
						||
| 
								 | 
							
														segmentRefs[n] = ints[k].ref;
							 | 
						||
| 
								 | 
							
													n++;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Wall segment.
							 | 
						||
| 
								 | 
							
											const int imin = ints[k-1].tmax;
							 | 
						||
| 
								 | 
							
											const int imax = ints[k].tmin;
							 | 
						||
| 
								 | 
							
											if (imin != imax)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												const float tmin = imin/255.0f; 
							 | 
						||
| 
								 | 
							
												const float tmax = imax/255.0f; 
							 | 
						||
| 
								 | 
							
												if (n < maxSegments)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													float* seg = &segmentVerts[n*6];
							 | 
						||
| 
								 | 
							
													dtVlerp(seg+0, vj,vi, tmin);
							 | 
						||
| 
								 | 
							
													dtVlerp(seg+3, vj,vi, tmax);
							 | 
						||
| 
								 | 
							
													if (segmentRefs)
							 | 
						||
| 
								 | 
							
														segmentRefs[n] = 0;
							 | 
						||
| 
								 | 
							
													n++;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													status |= DT_BUFFER_TOO_SMALL;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*segmentCount = n;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @p hitPos is not adjusted using the height detail data.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// @p hitDist will equal the search radius if there is no wall within the 
							 | 
						||
| 
								 | 
							
								/// radius. In this case the values of @p hitPos and @p hitNormal are
							 | 
						||
| 
								 | 
							
								/// undefined.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The normal will become unpredicable if @p hitDist is a very small number.
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
							 | 
						||
| 
								 | 
							
																			const dtQueryFilter* filter,
							 | 
						||
| 
								 | 
							
																			float* hitDist, float* hitPos, float* hitNormal) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									dtAssert(m_nav);
							 | 
						||
| 
								 | 
							
									dtAssert(m_nodePool);
							 | 
						||
| 
								 | 
							
									dtAssert(m_openList);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Validate input
							 | 
						||
| 
								 | 
							
									if (!m_nav->isValidPolyRef(startRef) ||
							 | 
						||
| 
								 | 
							
										!centerPos || !dtVisfinite(centerPos) ||
							 | 
						||
| 
								 | 
							
										maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
							 | 
						||
| 
								 | 
							
										!filter || !hitDist || !hitPos || !hitNormal)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										return DT_FAILURE | DT_INVALID_PARAM;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									m_nodePool->clear();
							 | 
						||
| 
								 | 
							
									m_openList->clear();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* startNode = m_nodePool->getNode(startRef);
							 | 
						||
| 
								 | 
							
									dtVcopy(startNode->pos, centerPos);
							 | 
						||
| 
								 | 
							
									startNode->pidx = 0;
							 | 
						||
| 
								 | 
							
									startNode->cost = 0;
							 | 
						||
| 
								 | 
							
									startNode->total = 0;
							 | 
						||
| 
								 | 
							
									startNode->id = startRef;
							 | 
						||
| 
								 | 
							
									startNode->flags = DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
									m_openList->push(startNode);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									float radiusSqr = dtSqr(maxRadius);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtStatus status = DT_SUCCESS;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									while (!m_openList->empty())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										dtNode* bestNode = m_openList->pop();
							 | 
						||
| 
								 | 
							
										bestNode->flags &= ~DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
										bestNode->flags |= DT_NODE_CLOSED;
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get poly and tile.
							 | 
						||
| 
								 | 
							
										// The API input has been cheked already, skip checking internal data.
							 | 
						||
| 
								 | 
							
										const dtPolyRef bestRef = bestNode->id;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* bestTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* bestPoly = 0;
							 | 
						||
| 
								 | 
							
										m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get parent poly and tile.
							 | 
						||
| 
								 | 
							
										dtPolyRef parentRef = 0;
							 | 
						||
| 
								 | 
							
										const dtMeshTile* parentTile = 0;
							 | 
						||
| 
								 | 
							
										const dtPoly* parentPoly = 0;
							 | 
						||
| 
								 | 
							
										if (bestNode->pidx)
							 | 
						||
| 
								 | 
							
											parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
							 | 
						||
| 
								 | 
							
										if (parentRef)
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Hit test walls.
							 | 
						||
| 
								 | 
							
										for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											// Skip non-solid edges.
							 | 
						||
| 
								 | 
							
											if (bestPoly->neis[j] & DT_EXT_LINK)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Tile border.
							 | 
						||
| 
								 | 
							
												bool solid = true;
							 | 
						||
| 
								 | 
							
												for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next)
							 | 
						||
| 
								 | 
							
												{
							 | 
						||
| 
								 | 
							
													const dtLink* link = &bestTile->links[k];
							 | 
						||
| 
								 | 
							
													if (link->edge == j)
							 | 
						||
| 
								 | 
							
													{
							 | 
						||
| 
								 | 
							
														if (link->ref != 0)
							 | 
						||
| 
								 | 
							
														{
							 | 
						||
| 
								 | 
							
															const dtMeshTile* neiTile = 0;
							 | 
						||
| 
								 | 
							
															const dtPoly* neiPoly = 0;
							 | 
						||
| 
								 | 
							
															m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
							 | 
						||
| 
								 | 
							
															if (filter->passFilter(link->ref, neiTile, neiPoly))
							 | 
						||
| 
								 | 
							
																solid = false;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												if (!solid) continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else if (bestPoly->neis[j])
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												// Internal edge
							 | 
						||
| 
								 | 
							
												const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1);
							 | 
						||
| 
								 | 
							
												const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx;
							 | 
						||
| 
								 | 
							
												if (filter->passFilter(ref, bestTile, &bestTile->polys[idx]))
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Calc distance to the edge.
							 | 
						||
| 
								 | 
							
											const float* vj = &bestTile->verts[bestPoly->verts[j]*3];
							 | 
						||
| 
								 | 
							
											const float* vi = &bestTile->verts[bestPoly->verts[i]*3];
							 | 
						||
| 
								 | 
							
											float tseg;
							 | 
						||
| 
								 | 
							
											float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Edge is too far, skip.
							 | 
						||
| 
								 | 
							
											if (distSqr > radiusSqr)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Hit wall, update radius.
							 | 
						||
| 
								 | 
							
											radiusSqr = distSqr;
							 | 
						||
| 
								 | 
							
											// Calculate hit pos.
							 | 
						||
| 
								 | 
							
											hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg;
							 | 
						||
| 
								 | 
							
											hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg;
							 | 
						||
| 
								 | 
							
											hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											const dtLink* link = &bestTile->links[i];
							 | 
						||
| 
								 | 
							
											dtPolyRef neighbourRef = link->ref;
							 | 
						||
| 
								 | 
							
											// Skip invalid neighbours and do not follow back to parent.
							 | 
						||
| 
								 | 
							
											if (!neighbourRef || neighbourRef == parentRef)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Expand to neighbour.
							 | 
						||
| 
								 | 
							
											const dtMeshTile* neighbourTile = 0;
							 | 
						||
| 
								 | 
							
											const dtPoly* neighbourPoly = 0;
							 | 
						||
| 
								 | 
							
											m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Skip off-mesh connections.
							 | 
						||
| 
								 | 
							
											if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Calc distance to the edge.
							 | 
						||
| 
								 | 
							
											const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3];
							 | 
						||
| 
								 | 
							
											const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3];
							 | 
						||
| 
								 | 
							
											float tseg;
							 | 
						||
| 
								 | 
							
											float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// If the circle is not touching the next polygon, skip it.
							 | 
						||
| 
								 | 
							
											if (distSqr > radiusSqr)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
							 | 
						||
| 
								 | 
							
											if (!neighbourNode)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												status |= DT_OUT_OF_NODES;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Cost
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags == 0)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												getEdgeMidPoint(bestRef, bestPoly, bestTile,
							 | 
						||
| 
								 | 
							
																neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// The node is already in open list and the new result is worse, skip.
							 | 
						||
| 
								 | 
							
											if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											neighbourNode->id = neighbourRef;
							 | 
						||
| 
								 | 
							
											neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
							 | 
						||
| 
								 | 
							
											neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
							 | 
						||
| 
								 | 
							
											neighbourNode->total = total;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
											if (neighbourNode->flags & DT_NODE_OPEN)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												m_openList->modify(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												neighbourNode->flags |= DT_NODE_OPEN;
							 | 
						||
| 
								 | 
							
												m_openList->push(neighbourNode);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// Calc hit normal.
							 | 
						||
| 
								 | 
							
									dtVsub(hitNormal, centerPos, hitPos);
							 | 
						||
| 
								 | 
							
									dtVnormalize(hitNormal);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									*hitDist = dtMathSqrtf(radiusSqr);
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									return status;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									const dtMeshTile* tile = 0;
							 | 
						||
| 
								 | 
							
									const dtPoly* poly = 0;
							 | 
						||
| 
								 | 
							
									dtStatus status = m_nav->getTileAndPolyByRef(ref, &tile, &poly);
							 | 
						||
| 
								 | 
							
									// If cannot get polygon, assume it does not exists and boundary is invalid.
							 | 
						||
| 
								 | 
							
									if (dtStatusFailed(status))
							 | 
						||
| 
								 | 
							
										return false;
							 | 
						||
| 
								 | 
							
									// If cannot pass filter, assume flags has changed and boundary is invalid.
							 | 
						||
| 
								 | 
							
									if (!filter->passFilter(ref, tile, poly))
							 | 
						||
| 
								 | 
							
										return false;
							 | 
						||
| 
								 | 
							
									return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/// @par
							 | 
						||
| 
								 | 
							
								///
							 | 
						||
| 
								 | 
							
								/// The closed list is the list of polygons that were fully evaluated during 
							 | 
						||
| 
								 | 
							
								/// the last navigation graph search. (A* or Dijkstra)
							 | 
						||
| 
								 | 
							
								/// 
							 | 
						||
| 
								 | 
							
								bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!m_nodePool) return false;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									dtNode* nodes[DT_MAX_STATES_PER_NODE];
							 | 
						||
| 
								 | 
							
									int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for (int i=0; i<n; i++)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										if (nodes[i]->flags & DT_NODE_CLOSED)
							 | 
						||
| 
								 | 
							
											return true;
							 | 
						||
| 
								 | 
							
									}		
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return false;
							 | 
						||
| 
								 | 
							
								}
							 |