Update Files
This commit is contained in:
		
							
								
								
									
										36
									
								
								lib/haxerecast/recastnavigation/DetourCrowd/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/haxerecast/recastnavigation/DetourCrowd/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
file(GLOB SOURCES Source/*.cpp)
 | 
			
		||||
add_library(DetourCrowd ${SOURCES})
 | 
			
		||||
 | 
			
		||||
add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd)
 | 
			
		||||
set_target_properties(DetourCrowd PROPERTIES DEBUG_POSTFIX -d)
 | 
			
		||||
 | 
			
		||||
set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
 | 
			
		||||
 | 
			
		||||
target_include_directories(DetourCrowd PUBLIC
 | 
			
		||||
    "$<BUILD_INTERFACE:${DetourCrowd_INCLUDE_DIR}>"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(DetourCrowd
 | 
			
		||||
    Detour
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set_target_properties(DetourCrowd PROPERTIES
 | 
			
		||||
        SOVERSION ${SOVERSION}
 | 
			
		||||
        VERSION ${LIB_VERSION}
 | 
			
		||||
        COMPILE_PDB_OUTPUT_DIRECTORY .
 | 
			
		||||
        COMPILE_PDB_NAME "DetourCrowd-d"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
install(TARGETS DetourCrowd
 | 
			
		||||
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 | 
			
		||||
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 | 
			
		||||
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
 | 
			
		||||
        COMPONENT library
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
file(GLOB INCLUDES Include/*.h)
 | 
			
		||||
install(FILES ${INCLUDES} DESTINATION
 | 
			
		||||
    ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
 | 
			
		||||
if(MSVC)
 | 
			
		||||
    install(FILES "$<TARGET_FILE_DIR:DetourCrowd>/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
 | 
			
		||||
endif()
 | 
			
		||||
@ -0,0 +1,460 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOURCROWD_H
 | 
			
		||||
#define DETOURCROWD_H
 | 
			
		||||
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
#include "DetourObstacleAvoidance.h"
 | 
			
		||||
#include "DetourLocalBoundary.h"
 | 
			
		||||
#include "DetourPathCorridor.h"
 | 
			
		||||
#include "DetourProximityGrid.h"
 | 
			
		||||
#include "DetourPathQueue.h"
 | 
			
		||||
 | 
			
		||||
/// The maximum number of neighbors that a crowd agent can take into account
 | 
			
		||||
/// for steering decisions.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
static const int DT_CROWDAGENT_MAX_NEIGHBOURS = 6;
 | 
			
		||||
 | 
			
		||||
/// The maximum number of corners a crowd agent will look ahead in the path.
 | 
			
		||||
/// This value is used for sizing the crowd agent corner buffers.
 | 
			
		||||
/// Due to the behavior of the crowd manager, the actual number of useful
 | 
			
		||||
/// corners will be one less than this number.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
static const int DT_CROWDAGENT_MAX_CORNERS = 4;
 | 
			
		||||
 | 
			
		||||
/// The maximum number of crowd avoidance configurations supported by the
 | 
			
		||||
/// crowd manager.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
/// @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), dtCrowd::getObstacleAvoidanceParams(),
 | 
			
		||||
///		 dtCrowdAgentParams::obstacleAvoidanceType
 | 
			
		||||
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
 | 
			
		||||
 | 
			
		||||
/// The maximum number of query filter types supported by the crowd manager.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
/// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
 | 
			
		||||
///		dtCrowdAgentParams::queryFilterType
 | 
			
		||||
static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
 | 
			
		||||
 | 
			
		||||
/// Provides neighbor data for agents managed by the crowd.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
/// @see dtCrowdAgent::neis, dtCrowd
 | 
			
		||||
struct dtCrowdNeighbour
 | 
			
		||||
{
 | 
			
		||||
	int idx;		///< The index of the neighbor in the crowd.
 | 
			
		||||
	float dist;		///< The distance between the current agent and the neighbor.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// The type of navigation mesh polygon the agent is currently traversing.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
enum CrowdAgentState
 | 
			
		||||
{
 | 
			
		||||
	DT_CROWDAGENT_STATE_INVALID,		///< The agent is not in a valid state.
 | 
			
		||||
	DT_CROWDAGENT_STATE_WALKING,		///< The agent is traversing a normal navigation mesh polygon.
 | 
			
		||||
	DT_CROWDAGENT_STATE_OFFMESH,		///< The agent is traversing an off-mesh connection.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Configuration parameters for a crowd agent.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
struct dtCrowdAgentParams
 | 
			
		||||
{
 | 
			
		||||
	float radius;						///< Agent radius. [Limit: >= 0]
 | 
			
		||||
	float height;						///< Agent height. [Limit: > 0]
 | 
			
		||||
	float maxAcceleration;				///< Maximum allowed acceleration. [Limit: >= 0]
 | 
			
		||||
	float maxSpeed;						///< Maximum allowed speed. [Limit: >= 0]
 | 
			
		||||
 | 
			
		||||
	/// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
 | 
			
		||||
	float collisionQueryRange;
 | 
			
		||||
 | 
			
		||||
	float pathOptimizationRange;		///< The path visibility optimization range. [Limit: > 0]
 | 
			
		||||
 | 
			
		||||
	/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
 | 
			
		||||
	float separationWeight;
 | 
			
		||||
 | 
			
		||||
	/// Flags that impact steering behavior. (See: #UpdateFlags)
 | 
			
		||||
	unsigned char updateFlags;
 | 
			
		||||
 | 
			
		||||
	/// The index of the avoidance configuration to use for the agent. 
 | 
			
		||||
	/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
 | 
			
		||||
	unsigned char obstacleAvoidanceType;	
 | 
			
		||||
 | 
			
		||||
	/// The index of the query filter used by this agent.
 | 
			
		||||
	unsigned char queryFilterType;
 | 
			
		||||
 | 
			
		||||
	/// User defined data attached to the agent.
 | 
			
		||||
	void* userData;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum MoveRequestState
 | 
			
		||||
{
 | 
			
		||||
	DT_CROWDAGENT_TARGET_NONE = 0,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_FAILED,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_VALID,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_REQUESTING,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
 | 
			
		||||
	DT_CROWDAGENT_TARGET_VELOCITY,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Represents an agent managed by a #dtCrowd object.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
struct dtCrowdAgent
 | 
			
		||||
{
 | 
			
		||||
	/// True if the agent is active, false if the agent is in an unused slot in the agent pool.
 | 
			
		||||
	bool active;
 | 
			
		||||
 | 
			
		||||
	/// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
 | 
			
		||||
	unsigned char state;
 | 
			
		||||
 | 
			
		||||
	/// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the requested position, else false.
 | 
			
		||||
	bool partial;
 | 
			
		||||
 | 
			
		||||
	/// The path corridor the agent is using.
 | 
			
		||||
	dtPathCorridor corridor;
 | 
			
		||||
 | 
			
		||||
	/// The local boundary data for the agent.
 | 
			
		||||
	dtLocalBoundary boundary;
 | 
			
		||||
	
 | 
			
		||||
	/// Time since the agent's path corridor was optimized.
 | 
			
		||||
	float topologyOptTime;
 | 
			
		||||
	
 | 
			
		||||
	/// The known neighbors of the agent.
 | 
			
		||||
	dtCrowdNeighbour neis[DT_CROWDAGENT_MAX_NEIGHBOURS];
 | 
			
		||||
 | 
			
		||||
	/// The number of neighbors.
 | 
			
		||||
	int nneis;
 | 
			
		||||
	
 | 
			
		||||
	/// The desired speed.
 | 
			
		||||
	float desiredSpeed;
 | 
			
		||||
 | 
			
		||||
	float npos[3];		///< The current agent position. [(x, y, z)]
 | 
			
		||||
	float disp[3];		///< A temporary value used to accumulate agent displacement during iterative collision resolution. [(x, y, z)]
 | 
			
		||||
	float dvel[3];		///< The desired velocity of the agent. Based on the current path, calculated from scratch each frame. [(x, y, z)]
 | 
			
		||||
	float nvel[3];		///< The desired velocity adjusted by obstacle avoidance, calculated from scratch each frame. [(x, y, z)]
 | 
			
		||||
	float vel[3];		///< The actual velocity of the agent. The change from nvel -> vel is constrained by max acceleration. [(x, y, z)]
 | 
			
		||||
 | 
			
		||||
	/// The agent's configuration parameters.
 | 
			
		||||
	dtCrowdAgentParams params;
 | 
			
		||||
 | 
			
		||||
	/// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
 | 
			
		||||
	float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
 | 
			
		||||
 | 
			
		||||
	/// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners]
 | 
			
		||||
	unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];
 | 
			
		||||
 | 
			
		||||
	/// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners]
 | 
			
		||||
	dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];
 | 
			
		||||
 | 
			
		||||
	/// The number of corners.
 | 
			
		||||
	int ncorners;
 | 
			
		||||
	
 | 
			
		||||
	unsigned char targetState;			///< State of the movement request.
 | 
			
		||||
	dtPolyRef targetRef;				///< Target polyref of the movement request.
 | 
			
		||||
	float targetPos[3];					///< Target position of the movement request (or velocity in case of DT_CROWDAGENT_TARGET_VELOCITY).
 | 
			
		||||
	dtPathQueueRef targetPathqRef;		///< Path finder ref.
 | 
			
		||||
	bool targetReplan;					///< Flag indicating that the current path is being replanned.
 | 
			
		||||
	float targetReplanTime;				/// <Time since the agent's target was replanned.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct dtCrowdAgentAnimation
 | 
			
		||||
{
 | 
			
		||||
	bool active;
 | 
			
		||||
	float initPos[3], startPos[3], endPos[3];
 | 
			
		||||
	dtPolyRef polyRef;
 | 
			
		||||
	float t, tmax;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Crowd agent update flags.
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
/// @see dtCrowdAgentParams::updateFlags
 | 
			
		||||
enum UpdateFlags
 | 
			
		||||
{
 | 
			
		||||
	DT_CROWD_ANTICIPATE_TURNS = 1,
 | 
			
		||||
	DT_CROWD_OBSTACLE_AVOIDANCE = 2,
 | 
			
		||||
	DT_CROWD_SEPARATION = 4,
 | 
			
		||||
	DT_CROWD_OPTIMIZE_VIS = 8,			///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
 | 
			
		||||
	DT_CROWD_OPTIMIZE_TOPO = 16,		///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct dtCrowdAgentDebugInfo
 | 
			
		||||
{
 | 
			
		||||
	int idx;
 | 
			
		||||
	float optStart[3], optEnd[3];
 | 
			
		||||
	dtObstacleAvoidanceDebugData* vod;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Provides local steering behaviors for a group of agents. 
 | 
			
		||||
/// @ingroup crowd
 | 
			
		||||
class dtCrowd
 | 
			
		||||
{
 | 
			
		||||
	int m_maxAgents;
 | 
			
		||||
	dtCrowdAgent* m_agents;
 | 
			
		||||
	dtCrowdAgent** m_activeAgents;
 | 
			
		||||
	dtCrowdAgentAnimation* m_agentAnims;
 | 
			
		||||
	
 | 
			
		||||
	dtPathQueue m_pathq;
 | 
			
		||||
 | 
			
		||||
	dtObstacleAvoidanceParams m_obstacleQueryParams[DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS];
 | 
			
		||||
	dtObstacleAvoidanceQuery* m_obstacleQuery;
 | 
			
		||||
	
 | 
			
		||||
	dtProximityGrid* m_grid;
 | 
			
		||||
	
 | 
			
		||||
	dtPolyRef* m_pathResult;
 | 
			
		||||
	int m_maxPathResult;
 | 
			
		||||
	
 | 
			
		||||
	float m_agentPlacementHalfExtents[3];
 | 
			
		||||
 | 
			
		||||
	dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
 | 
			
		||||
 | 
			
		||||
	float m_maxAgentRadius;
 | 
			
		||||
 | 
			
		||||
	int m_velocitySampleCount;
 | 
			
		||||
 | 
			
		||||
	dtNavMeshQuery* m_navquery;
 | 
			
		||||
 | 
			
		||||
	void updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt);
 | 
			
		||||
	void updateMoveRequest(const float dt);
 | 
			
		||||
	void checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt);
 | 
			
		||||
 | 
			
		||||
	inline int getAgentIndex(const dtCrowdAgent* agent) const  { return (int)(agent - m_agents); }
 | 
			
		||||
 | 
			
		||||
	bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
 | 
			
		||||
 | 
			
		||||
	void purge();
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	dtCrowd();
 | 
			
		||||
	~dtCrowd();
 | 
			
		||||
	
 | 
			
		||||
	/// Initializes the crowd.  
 | 
			
		||||
	///  @param[in]		maxAgents		The maximum number of agents the crowd can manage. [Limit: >= 1]
 | 
			
		||||
	///  @param[in]		maxAgentRadius	The maximum radius of any agent that will be added to the crowd. [Limit: > 0]
 | 
			
		||||
	///  @param[in]		nav				The navigation mesh to use for planning.
 | 
			
		||||
	/// @return True if the initialization succeeded.
 | 
			
		||||
	bool init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav);
 | 
			
		||||
	
 | 
			
		||||
	/// Sets the shared avoidance configuration for the specified index.
 | 
			
		||||
	///  @param[in]		idx		The index. [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
 | 
			
		||||
	///  @param[in]		params	The new configuration.
 | 
			
		||||
	void setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params);
 | 
			
		||||
 | 
			
		||||
	/// Gets the shared avoidance configuration for the specified index.
 | 
			
		||||
	///  @param[in]		idx		The index of the configuration to retreive. 
 | 
			
		||||
	///							[Limits:  0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
 | 
			
		||||
	/// @return The requested configuration.
 | 
			
		||||
	const dtObstacleAvoidanceParams* getObstacleAvoidanceParams(const int idx) const;
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the specified agent from the pool.
 | 
			
		||||
	///	 @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	/// @return The requested agent.
 | 
			
		||||
	const dtCrowdAgent* getAgent(const int idx);
 | 
			
		||||
 | 
			
		||||
	/// Gets the specified agent from the pool.
 | 
			
		||||
	///	 @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	/// @return The requested agent.
 | 
			
		||||
	dtCrowdAgent* getEditableAgent(const int idx);
 | 
			
		||||
 | 
			
		||||
	/// The maximum number of agents that can be managed by the object.
 | 
			
		||||
	/// @return The maximum number of agents.
 | 
			
		||||
	int getAgentCount() const;
 | 
			
		||||
	
 | 
			
		||||
	/// Adds a new agent to the crowd.
 | 
			
		||||
	///  @param[in]		pos		The requested position of the agent. [(x, y, z)]
 | 
			
		||||
	///  @param[in]		params	The configutation of the agent.
 | 
			
		||||
	/// @return The index of the agent in the agent pool. Or -1 if the agent could not be added.
 | 
			
		||||
	int addAgent(const float* pos, const dtCrowdAgentParams* params);
 | 
			
		||||
 | 
			
		||||
	/// Updates the specified agent's configuration.
 | 
			
		||||
	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	///  @param[in]		params	The new agent configuration.
 | 
			
		||||
	void updateAgentParameters(const int idx, const dtCrowdAgentParams* params);
 | 
			
		||||
 | 
			
		||||
	/// Removes the agent from the crowd.
 | 
			
		||||
	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	void removeAgent(const int idx);
 | 
			
		||||
	
 | 
			
		||||
	/// Submits a new move request for the specified agent.
 | 
			
		||||
	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	///  @param[in]		ref		The position's polygon reference.
 | 
			
		||||
	///  @param[in]		pos		The position within the polygon. [(x, y, z)]
 | 
			
		||||
	/// @return True if the request was successfully submitted.
 | 
			
		||||
	bool requestMoveTarget(const int idx, dtPolyRef ref, const float* pos);
 | 
			
		||||
 | 
			
		||||
	/// Submits a new move request for the specified agent.
 | 
			
		||||
	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	///  @param[in]		vel		The movement velocity. [(x, y, z)]
 | 
			
		||||
	/// @return True if the request was successfully submitted.
 | 
			
		||||
	bool requestMoveVelocity(const int idx, const float* vel);
 | 
			
		||||
 | 
			
		||||
	/// Resets any request for the specified agent.
 | 
			
		||||
	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
 | 
			
		||||
	/// @return True if the request was successfully reseted.
 | 
			
		||||
	bool resetMoveTarget(const int idx);
 | 
			
		||||
 | 
			
		||||
	/// Gets the active agents int the agent pool.
 | 
			
		||||
	///  @param[out]	agents		An array of agent pointers. [(#dtCrowdAgent *) * maxAgents]
 | 
			
		||||
	///  @param[in]		maxAgents	The size of the crowd agent array.
 | 
			
		||||
	/// @return The number of agents returned in @p agents.
 | 
			
		||||
	int getActiveAgents(dtCrowdAgent** agents, const int maxAgents);
 | 
			
		||||
 | 
			
		||||
	/// Updates the steering and positions of all agents.
 | 
			
		||||
	///  @param[in]		dt		The time, in seconds, to update the simulation. [Limit: > 0]
 | 
			
		||||
	///  @param[out]	debug	A debug object to load with debug information. [Opt]
 | 
			
		||||
	void update(const float dt, dtCrowdAgentDebugInfo* debug);
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the filter used by the crowd.
 | 
			
		||||
	/// @return The filter used by the crowd.
 | 
			
		||||
	inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the filter used by the crowd.
 | 
			
		||||
	/// @return The filter used by the crowd.
 | 
			
		||||
	inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
 | 
			
		||||
 | 
			
		||||
	/// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations. 
 | 
			
		||||
	/// @return The search halfExtents used by the crowd. [(x, y, z)]
 | 
			
		||||
	const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; }
 | 
			
		||||
 | 
			
		||||
	/// Same as getQueryHalfExtents. Left to maintain backwards compatibility.
 | 
			
		||||
	/// @return The search halfExtents used by the crowd. [(x, y, z)]
 | 
			
		||||
	const float* getQueryExtents() const { return m_agentPlacementHalfExtents; }
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the velocity sample count.
 | 
			
		||||
	/// @return The velocity sample count.
 | 
			
		||||
	inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the crowd's proximity grid.
 | 
			
		||||
	/// @return The crowd's proximity grid.
 | 
			
		||||
	const dtProximityGrid* getGrid() const { return m_grid; }
 | 
			
		||||
 | 
			
		||||
	/// Gets the crowd's path request queue.
 | 
			
		||||
	/// @return The crowd's path request queue.
 | 
			
		||||
	const dtPathQueue* getPathQueue() const { return &m_pathq; }
 | 
			
		||||
 | 
			
		||||
	/// Gets the query object used by the crowd.
 | 
			
		||||
	const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtCrowd(const dtCrowd&);
 | 
			
		||||
	dtCrowd& operator=(const dtCrowd&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Allocates a crowd object using the Detour allocator.
 | 
			
		||||
/// @return A crowd object that is ready for initialization, or null on failure.
 | 
			
		||||
///  @ingroup crowd
 | 
			
		||||
dtCrowd* dtAllocCrowd();
 | 
			
		||||
 | 
			
		||||
/// Frees the specified crowd object using the Detour allocator.
 | 
			
		||||
///  @param[in]		ptr		A crowd object allocated using #dtAllocCrowd
 | 
			
		||||
///  @ingroup crowd
 | 
			
		||||
void dtFreeCrowd(dtCrowd* ptr);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // DETOURCROWD_H
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// This section contains detailed documentation for members that don't have
 | 
			
		||||
// a source file. It reduces clutter in the main section of the header.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@defgroup crowd Crowd
 | 
			
		||||
 | 
			
		||||
Members in this module implement local steering and dynamic avoidance features.
 | 
			
		||||
 | 
			
		||||
The crowd is the big beast of the navigation features. It not only handles a 
 | 
			
		||||
lot of the path management for you, but also local steering and dynamic 
 | 
			
		||||
avoidance between members of the crowd. I.e. It can keep your agents from 
 | 
			
		||||
running into each other.
 | 
			
		||||
 | 
			
		||||
Main class: #dtCrowd
 | 
			
		||||
 | 
			
		||||
The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy 
 | 
			
		||||
to use path planning features. But in the end they only give you points that 
 | 
			
		||||
your navigation client should be moving toward. When it comes to deciding things 
 | 
			
		||||
like agent velocity and steering to avoid other agents, that is up to you to 
 | 
			
		||||
implement. Unless, of course, you decide to use #dtCrowd.
 | 
			
		||||
 | 
			
		||||
Basically, you add an agent to the crowd, providing various configuration 
 | 
			
		||||
settings such as maximum speed and acceleration. You also provide a local 
 | 
			
		||||
target to more toward. The crowd manager then provides, with every update, the 
 | 
			
		||||
new agent position and velocity for the frame. The movement will be 
 | 
			
		||||
constrained to the navigation mesh, and steering will be applied to ensure 
 | 
			
		||||
agents managed by the crowd do not collide with each other.
 | 
			
		||||
 | 
			
		||||
This is very powerful feature set. But it comes with limitations.
 | 
			
		||||
 | 
			
		||||
The biggest limitation is that you must give control of the agent's position 
 | 
			
		||||
completely over to the crowd manager. You can update things like maximum speed 
 | 
			
		||||
and acceleration. But in order for the crowd manager to do its thing, it can't 
 | 
			
		||||
allow you to constantly be giving it overrides to position and velocity. So 
 | 
			
		||||
you give up direct control of the agent's movement. It belongs to the crowd.
 | 
			
		||||
 | 
			
		||||
The second biggest limitation revolves around the fact that the crowd manager 
 | 
			
		||||
deals with local planning. So the agent's target should never be more than 
 | 
			
		||||
256 polygons aways from its current position. If it is, you risk 
 | 
			
		||||
your agent failing to reach its target. So you may still need to do long 
 | 
			
		||||
distance planning and provide the crowd manager with intermediate targets.
 | 
			
		||||
 | 
			
		||||
Other significant limitations:
 | 
			
		||||
 | 
			
		||||
- All agents using the crowd manager will use the same #dtQueryFilter.
 | 
			
		||||
- Crowd management is relatively expensive. The maximum agents under crowd 
 | 
			
		||||
  management at any one time is between 20 and 30.  A good place to start
 | 
			
		||||
  is a maximum of 25 agents for 0.5ms per frame.
 | 
			
		||||
 | 
			
		||||
@note This is a summary list of members.  Use the index or search 
 | 
			
		||||
feature to find minor members.
 | 
			
		||||
 | 
			
		||||
@struct dtCrowdAgentParams
 | 
			
		||||
@see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters()
 | 
			
		||||
 | 
			
		||||
@var dtCrowdAgentParams::obstacleAvoidanceType
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
#dtCrowd permits agents to use different avoidance configurations.  This value 
 | 
			
		||||
is the index of the #dtObstacleAvoidanceParams within the crowd.
 | 
			
		||||
 | 
			
		||||
@see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), 
 | 
			
		||||
	 dtCrowd::getObstacleAvoidanceParams()
 | 
			
		||||
 | 
			
		||||
@var dtCrowdAgentParams::collisionQueryRange
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Collision elements include other agents and navigation mesh boundaries.
 | 
			
		||||
 | 
			
		||||
This value is often based on the agent radius and/or maximum speed. E.g. radius * 8
 | 
			
		||||
 | 
			
		||||
@var dtCrowdAgentParams::pathOptimizationRange
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag.
 | 
			
		||||
 | 
			
		||||
This value is often based on the agent radius. E.g. radius * 30
 | 
			
		||||
 | 
			
		||||
@see dtPathCorridor::optimizePathVisibility()
 | 
			
		||||
 | 
			
		||||
@var dtCrowdAgentParams::separationWeight
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
A higher value will result in agents trying to stay farther away from each other at 
 | 
			
		||||
the cost of more difficult steering in tight spaces.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOURLOCALBOUNDARY_H
 | 
			
		||||
#define DETOURLOCALBOUNDARY_H
 | 
			
		||||
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class dtLocalBoundary
 | 
			
		||||
{
 | 
			
		||||
	static const int MAX_LOCAL_SEGS = 8;
 | 
			
		||||
	static const int MAX_LOCAL_POLYS = 16;
 | 
			
		||||
	
 | 
			
		||||
	struct Segment
 | 
			
		||||
	{
 | 
			
		||||
		float s[6];	///< Segment start/end
 | 
			
		||||
		float d;	///< Distance for pruning.
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	float m_center[3];
 | 
			
		||||
	Segment m_segs[MAX_LOCAL_SEGS];
 | 
			
		||||
	int m_nsegs;
 | 
			
		||||
	
 | 
			
		||||
	dtPolyRef m_polys[MAX_LOCAL_POLYS];
 | 
			
		||||
	int m_npolys;
 | 
			
		||||
 | 
			
		||||
	void addSegment(const float dist, const float* s);
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	dtLocalBoundary();
 | 
			
		||||
	~dtLocalBoundary();
 | 
			
		||||
	
 | 
			
		||||
	void reset();
 | 
			
		||||
	
 | 
			
		||||
	void update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
 | 
			
		||||
				dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	bool isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	inline const float* getCenter() const { return m_center; }
 | 
			
		||||
	inline int getSegmentCount() const { return m_nsegs; }
 | 
			
		||||
	inline const float* getSegment(int i) const { return m_segs[i].s; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtLocalBoundary(const dtLocalBoundary&);
 | 
			
		||||
	dtLocalBoundary& operator=(const dtLocalBoundary&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // DETOURLOCALBOUNDARY_H
 | 
			
		||||
@ -0,0 +1,159 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOUROBSTACLEAVOIDANCE_H
 | 
			
		||||
#define DETOUROBSTACLEAVOIDANCE_H
 | 
			
		||||
 | 
			
		||||
struct dtObstacleCircle
 | 
			
		||||
{
 | 
			
		||||
	float p[3];				///< Position of the obstacle
 | 
			
		||||
	float vel[3];			///< Velocity of the obstacle
 | 
			
		||||
	float dvel[3];			///< Velocity of the obstacle
 | 
			
		||||
	float rad;				///< Radius of the obstacle
 | 
			
		||||
	float dp[3], np[3];		///< Use for side selection during sampling.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct dtObstacleSegment
 | 
			
		||||
{
 | 
			
		||||
	float p[3], q[3];		///< End points of the obstacle segment
 | 
			
		||||
	bool touch;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class dtObstacleAvoidanceDebugData
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dtObstacleAvoidanceDebugData();
 | 
			
		||||
	~dtObstacleAvoidanceDebugData();
 | 
			
		||||
	
 | 
			
		||||
	bool init(const int maxSamples);
 | 
			
		||||
	void reset();
 | 
			
		||||
	void addSample(const float* vel, const float ssize, const float pen,
 | 
			
		||||
				   const float vpen, const float vcpen, const float spen, const float tpen);
 | 
			
		||||
	
 | 
			
		||||
	void normalizeSamples();
 | 
			
		||||
	
 | 
			
		||||
	inline int getSampleCount() const { return m_nsamples; }
 | 
			
		||||
	inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
 | 
			
		||||
	inline float getSampleSize(const int i) const { return m_ssize[i]; }
 | 
			
		||||
	inline float getSamplePenalty(const int i) const { return m_pen[i]; }
 | 
			
		||||
	inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
 | 
			
		||||
	inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
 | 
			
		||||
	inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
 | 
			
		||||
	inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtObstacleAvoidanceDebugData(const dtObstacleAvoidanceDebugData&);
 | 
			
		||||
	dtObstacleAvoidanceDebugData& operator=(const dtObstacleAvoidanceDebugData&);
 | 
			
		||||
 | 
			
		||||
	int m_nsamples;
 | 
			
		||||
	int m_maxSamples;
 | 
			
		||||
	float* m_vel;
 | 
			
		||||
	float* m_ssize;
 | 
			
		||||
	float* m_pen;
 | 
			
		||||
	float* m_vpen;
 | 
			
		||||
	float* m_vcpen;
 | 
			
		||||
	float* m_spen;
 | 
			
		||||
	float* m_tpen;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
 | 
			
		||||
void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const int DT_MAX_PATTERN_DIVS = 32;	///< Max numver of adaptive divs.
 | 
			
		||||
static const int DT_MAX_PATTERN_RINGS = 4;	///< Max number of adaptive rings.
 | 
			
		||||
 | 
			
		||||
struct dtObstacleAvoidanceParams
 | 
			
		||||
{
 | 
			
		||||
	float velBias;
 | 
			
		||||
	float weightDesVel;
 | 
			
		||||
	float weightCurVel;
 | 
			
		||||
	float weightSide;
 | 
			
		||||
	float weightToi;
 | 
			
		||||
	float horizTime;
 | 
			
		||||
	unsigned char gridSize;	///< grid
 | 
			
		||||
	unsigned char adaptiveDivs;	///< adaptive
 | 
			
		||||
	unsigned char adaptiveRings;	///< adaptive
 | 
			
		||||
	unsigned char adaptiveDepth;	///< adaptive
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class dtObstacleAvoidanceQuery
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	dtObstacleAvoidanceQuery();
 | 
			
		||||
	~dtObstacleAvoidanceQuery();
 | 
			
		||||
	
 | 
			
		||||
	bool init(const int maxCircles, const int maxSegments);
 | 
			
		||||
	
 | 
			
		||||
	void reset();
 | 
			
		||||
 | 
			
		||||
	void addCircle(const float* pos, const float rad,
 | 
			
		||||
				   const float* vel, const float* dvel);
 | 
			
		||||
				   
 | 
			
		||||
	void addSegment(const float* p, const float* q);
 | 
			
		||||
 | 
			
		||||
	int sampleVelocityGrid(const float* pos, const float rad, const float vmax,
 | 
			
		||||
						   const float* vel, const float* dvel, float* nvel,
 | 
			
		||||
						   const dtObstacleAvoidanceParams* params,
 | 
			
		||||
						   dtObstacleAvoidanceDebugData* debug = 0);
 | 
			
		||||
 | 
			
		||||
	int sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
 | 
			
		||||
							   const float* vel, const float* dvel, float* nvel,
 | 
			
		||||
							   const dtObstacleAvoidanceParams* params, 
 | 
			
		||||
							   dtObstacleAvoidanceDebugData* debug = 0);
 | 
			
		||||
	
 | 
			
		||||
	inline int getObstacleCircleCount() const { return m_ncircles; }
 | 
			
		||||
	const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
 | 
			
		||||
 | 
			
		||||
	inline int getObstacleSegmentCount() const { return m_nsegments; }
 | 
			
		||||
	const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtObstacleAvoidanceQuery(const dtObstacleAvoidanceQuery&);
 | 
			
		||||
	dtObstacleAvoidanceQuery& operator=(const dtObstacleAvoidanceQuery&);
 | 
			
		||||
 | 
			
		||||
	void prepare(const float* pos, const float* dvel);
 | 
			
		||||
 | 
			
		||||
	float processSample(const float* vcand, const float cs,
 | 
			
		||||
						const float* pos, const float rad,
 | 
			
		||||
						const float* vel, const float* dvel,
 | 
			
		||||
						const float minPenalty,
 | 
			
		||||
						dtObstacleAvoidanceDebugData* debug);
 | 
			
		||||
 | 
			
		||||
	dtObstacleAvoidanceParams m_params;
 | 
			
		||||
	float m_invHorizTime;
 | 
			
		||||
	float m_vmax;
 | 
			
		||||
	float m_invVmax;
 | 
			
		||||
 | 
			
		||||
	int m_maxCircles;
 | 
			
		||||
	dtObstacleCircle* m_circles;
 | 
			
		||||
	int m_ncircles;
 | 
			
		||||
 | 
			
		||||
	int m_maxSegments;
 | 
			
		||||
	dtObstacleSegment* m_segments;
 | 
			
		||||
	int m_nsegments;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
 | 
			
		||||
void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // DETOUROBSTACLEAVOIDANCE_H
 | 
			
		||||
@ -0,0 +1,151 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOUTPATHCORRIDOR_H
 | 
			
		||||
#define DETOUTPATHCORRIDOR_H
 | 
			
		||||
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
 | 
			
		||||
/// Represents a dynamic polygon corridor used to plan agent movement.
 | 
			
		||||
/// @ingroup crowd, detour
 | 
			
		||||
class dtPathCorridor
 | 
			
		||||
{
 | 
			
		||||
	float m_pos[3];
 | 
			
		||||
	float m_target[3];
 | 
			
		||||
	
 | 
			
		||||
	dtPolyRef* m_path;
 | 
			
		||||
	int m_npath;
 | 
			
		||||
	int m_maxPath;
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	dtPathCorridor();
 | 
			
		||||
	~dtPathCorridor();
 | 
			
		||||
	
 | 
			
		||||
	/// Allocates the corridor's path buffer. 
 | 
			
		||||
	///  @param[in]		maxPath		The maximum path size the corridor can handle.
 | 
			
		||||
	/// @return True if the initialization succeeded.
 | 
			
		||||
	bool init(const int maxPath);
 | 
			
		||||
	
 | 
			
		||||
	/// Resets the path corridor to the specified position.
 | 
			
		||||
	///  @param[in]		ref		The polygon reference containing the position.
 | 
			
		||||
	///  @param[in]		pos		The new position in the corridor. [(x, y, z)]
 | 
			
		||||
	void reset(dtPolyRef ref, const float* pos);
 | 
			
		||||
	
 | 
			
		||||
	/// Finds the corners in the corridor from the position toward the target. (The straightened path.)
 | 
			
		||||
	///  @param[out]	cornerVerts		The corner vertices. [(x, y, z) * cornerCount] [Size: <= maxCorners]
 | 
			
		||||
	///  @param[out]	cornerFlags		The flag for each corner. [(flag) * cornerCount] [Size: <= maxCorners]
 | 
			
		||||
	///  @param[out]	cornerPolys		The polygon reference for each corner. [(polyRef) * cornerCount] 
 | 
			
		||||
	///  								[Size: <= @p maxCorners]
 | 
			
		||||
	///  @param[in]		maxCorners		The maximum number of corners the buffers can hold.
 | 
			
		||||
	///  @param[in]		navquery		The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter			The filter to apply to the operation.
 | 
			
		||||
	/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
 | 
			
		||||
	int findCorners(float* cornerVerts, unsigned char* cornerFlags,
 | 
			
		||||
					dtPolyRef* cornerPolys, const int maxCorners,
 | 
			
		||||
					dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	/// Attempts to optimize the path if the specified point is visible from the current position.
 | 
			
		||||
	///  @param[in]		next					The point to search toward. [(x, y, z])
 | 
			
		||||
	///  @param[in]		pathOptimizationRange	The maximum range to search. [Limit: > 0]
 | 
			
		||||
	///  @param[in]		navquery				The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter					The filter to apply to the operation.			
 | 
			
		||||
	void optimizePathVisibility(const float* next, const float pathOptimizationRange,
 | 
			
		||||
								dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	/// Attempts to optimize the path using a local area search. (Partial replanning.) 
 | 
			
		||||
	///  @param[in]		navquery	The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter		The filter to apply to the operation.	
 | 
			
		||||
	bool optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
 | 
			
		||||
								   float* startPos, float* endPos,
 | 
			
		||||
								   dtNavMeshQuery* navquery);
 | 
			
		||||
 | 
			
		||||
	bool fixPathStart(dtPolyRef safeRef, const float* safePos);
 | 
			
		||||
 | 
			
		||||
	bool trimInvalidPath(dtPolyRef safeRef, const float* safePos,
 | 
			
		||||
						 dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	/// Checks the current corridor path to see if its polygon references remain valid. 
 | 
			
		||||
	///  @param[in]		maxLookAhead	The number of polygons from the beginning of the corridor to search.
 | 
			
		||||
	///  @param[in]		navquery		The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter			The filter to apply to the operation.	
 | 
			
		||||
	bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	/// Moves the position from the current location to the desired location, adjusting the corridor 
 | 
			
		||||
	/// as needed to reflect the change.
 | 
			
		||||
	///  @param[in]		npos		The desired new position. [(x, y, z)]
 | 
			
		||||
	///  @param[in]		navquery	The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter		The filter to apply to the operation.
 | 
			
		||||
	/// @return Returns true if move succeeded.
 | 
			
		||||
	bool movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
 | 
			
		||||
	/// Moves the target from the curent location to the desired location, adjusting the corridor
 | 
			
		||||
	/// as needed to reflect the change. 
 | 
			
		||||
	///  @param[in]		npos		The desired new target position. [(x, y, z)]
 | 
			
		||||
	///  @param[in]		navquery	The query object used to build the corridor.
 | 
			
		||||
	///  @param[in]		filter		The filter to apply to the operation.
 | 
			
		||||
	/// @return Returns true if move succeeded.
 | 
			
		||||
	bool moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	/// Loads a new path and target into the corridor.
 | 
			
		||||
	///  @param[in]		target		The target location within the last polygon of the path. [(x, y, z)]
 | 
			
		||||
	///  @param[in]		path		The path corridor. [(polyRef) * @p npolys]
 | 
			
		||||
	///  @param[in]		npath		The number of polygons in the path.
 | 
			
		||||
	void setCorridor(const float* target, const dtPolyRef* polys, const int npath);
 | 
			
		||||
	
 | 
			
		||||
	/// Gets the current position within the corridor. (In the first polygon.)
 | 
			
		||||
	/// @return The current position within the corridor.
 | 
			
		||||
	inline const float* getPos() const { return m_pos; }
 | 
			
		||||
 | 
			
		||||
	/// Gets the current target within the corridor. (In the last polygon.)
 | 
			
		||||
	/// @return The current target within the corridor.
 | 
			
		||||
	inline const float* getTarget() const { return m_target; }
 | 
			
		||||
	
 | 
			
		||||
	/// The polygon reference id of the first polygon in the corridor, the polygon containing the position.
 | 
			
		||||
	/// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
 | 
			
		||||
	inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; }
 | 
			
		||||
 | 
			
		||||
	/// The polygon reference id of the last polygon in the corridor, the polygon containing the target.
 | 
			
		||||
	/// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
 | 
			
		||||
	inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; }
 | 
			
		||||
	
 | 
			
		||||
	/// The corridor's path.
 | 
			
		||||
	/// @return The corridor's path. [(polyRef) * #getPathCount()]
 | 
			
		||||
	inline const dtPolyRef* getPath() const { return m_path; }
 | 
			
		||||
 | 
			
		||||
	/// The number of polygons in the current corridor path.
 | 
			
		||||
	/// @return The number of polygons in the current corridor path.
 | 
			
		||||
	inline int getPathCount() const { return m_npath; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtPathCorridor(const dtPathCorridor&);
 | 
			
		||||
	dtPathCorridor& operator=(const dtPathCorridor&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
							  const dtPolyRef* visited, const int nvisited);
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
							const dtPolyRef* visited, const int nvisited);
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
								 const dtPolyRef* visited, const int nvisited);
 | 
			
		||||
 | 
			
		||||
#endif // DETOUTPATHCORRIDOR_H
 | 
			
		||||
@ -0,0 +1,79 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOURPATHQUEUE_H
 | 
			
		||||
#define DETOURPATHQUEUE_H
 | 
			
		||||
 | 
			
		||||
#include "DetourNavMesh.h"
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
 | 
			
		||||
static const unsigned int DT_PATHQ_INVALID = 0;
 | 
			
		||||
 | 
			
		||||
typedef unsigned int dtPathQueueRef;
 | 
			
		||||
 | 
			
		||||
class dtPathQueue
 | 
			
		||||
{
 | 
			
		||||
	struct PathQuery
 | 
			
		||||
	{
 | 
			
		||||
		dtPathQueueRef ref;
 | 
			
		||||
		/// Path find start and end location.
 | 
			
		||||
		float startPos[3], endPos[3];
 | 
			
		||||
		dtPolyRef startRef, endRef;
 | 
			
		||||
		/// Result.
 | 
			
		||||
		dtPolyRef* path;
 | 
			
		||||
		int npath;
 | 
			
		||||
		/// State.
 | 
			
		||||
		dtStatus status;
 | 
			
		||||
		int keepAlive;
 | 
			
		||||
		const dtQueryFilter* filter; ///< TODO: This is potentially dangerous!
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	static const int MAX_QUEUE = 8;
 | 
			
		||||
	PathQuery m_queue[MAX_QUEUE];
 | 
			
		||||
	dtPathQueueRef m_nextHandle;
 | 
			
		||||
	int m_maxPathSize;
 | 
			
		||||
	int m_queueHead;
 | 
			
		||||
	dtNavMeshQuery* m_navquery;
 | 
			
		||||
	
 | 
			
		||||
	void purge();
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	dtPathQueue();
 | 
			
		||||
	~dtPathQueue();
 | 
			
		||||
	
 | 
			
		||||
	bool init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav);
 | 
			
		||||
	
 | 
			
		||||
	void update(const int maxIters);
 | 
			
		||||
	
 | 
			
		||||
	dtPathQueueRef request(dtPolyRef startRef, dtPolyRef endRef,
 | 
			
		||||
						   const float* startPos, const float* endPos, 
 | 
			
		||||
						   const dtQueryFilter* filter);
 | 
			
		||||
	
 | 
			
		||||
	dtStatus getRequestStatus(dtPathQueueRef ref) const;
 | 
			
		||||
	
 | 
			
		||||
	dtStatus getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath);
 | 
			
		||||
	
 | 
			
		||||
	inline const dtNavMeshQuery* getNavQuery() const { return m_navquery; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtPathQueue(const dtPathQueue&);
 | 
			
		||||
	dtPathQueue& operator=(const dtPathQueue&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // DETOURPATHQUEUE_H
 | 
			
		||||
@ -0,0 +1,74 @@
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef DETOURPROXIMITYGRID_H
 | 
			
		||||
#define DETOURPROXIMITYGRID_H
 | 
			
		||||
 | 
			
		||||
class dtProximityGrid
 | 
			
		||||
{
 | 
			
		||||
	float m_cellSize;
 | 
			
		||||
	float m_invCellSize;
 | 
			
		||||
	
 | 
			
		||||
	struct Item
 | 
			
		||||
	{
 | 
			
		||||
		unsigned short id;
 | 
			
		||||
		short x,y;
 | 
			
		||||
		unsigned short next;
 | 
			
		||||
	};
 | 
			
		||||
	Item* m_pool;
 | 
			
		||||
	int m_poolHead;
 | 
			
		||||
	int m_poolSize;
 | 
			
		||||
	
 | 
			
		||||
	unsigned short* m_buckets;
 | 
			
		||||
	int m_bucketsSize;
 | 
			
		||||
	
 | 
			
		||||
	int m_bounds[4];
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	dtProximityGrid();
 | 
			
		||||
	~dtProximityGrid();
 | 
			
		||||
	
 | 
			
		||||
	bool init(const int poolSize, const float cellSize);
 | 
			
		||||
	
 | 
			
		||||
	void clear();
 | 
			
		||||
	
 | 
			
		||||
	void addItem(const unsigned short id,
 | 
			
		||||
				 const float minx, const float miny,
 | 
			
		||||
				 const float maxx, const float maxy);
 | 
			
		||||
	
 | 
			
		||||
	int queryItems(const float minx, const float miny,
 | 
			
		||||
				   const float maxx, const float maxy,
 | 
			
		||||
				   unsigned short* ids, const int maxIds) const;
 | 
			
		||||
	
 | 
			
		||||
	int getItemCountAt(const int x, const int y) const;
 | 
			
		||||
	
 | 
			
		||||
	inline const int* getBounds() const { return m_bounds; }
 | 
			
		||||
	inline float getCellSize() const { return m_cellSize; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// Explicitly disabled copy constructor and copy assignment operator.
 | 
			
		||||
	dtProximityGrid(const dtProximityGrid&);
 | 
			
		||||
	dtProximityGrid& operator=(const dtProximityGrid&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
dtProximityGrid* dtAllocProximityGrid();
 | 
			
		||||
void dtFreeProximityGrid(dtProximityGrid* ptr);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // DETOURPROXIMITYGRID_H
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1450
									
								
								lib/haxerecast/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1450
									
								
								lib/haxerecast/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -0,0 +1,137 @@
 | 
			
		||||
//
 | 
			
		||||
// 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 "DetourLocalBoundary.h"
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
#include "DetourCommon.h"
 | 
			
		||||
#include "DetourAssert.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtLocalBoundary::dtLocalBoundary() :
 | 
			
		||||
	m_nsegs(0),
 | 
			
		||||
	m_npolys(0)
 | 
			
		||||
{
 | 
			
		||||
	dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtLocalBoundary::~dtLocalBoundary()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtLocalBoundary::reset()
 | 
			
		||||
{
 | 
			
		||||
	dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
 | 
			
		||||
	m_npolys = 0;
 | 
			
		||||
	m_nsegs = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtLocalBoundary::addSegment(const float dist, const float* s)
 | 
			
		||||
{
 | 
			
		||||
	// Insert neighbour based on the distance.
 | 
			
		||||
	Segment* seg = 0;
 | 
			
		||||
	if (!m_nsegs)
 | 
			
		||||
	{
 | 
			
		||||
		// First, trivial accept.
 | 
			
		||||
		seg = &m_segs[0];
 | 
			
		||||
	}
 | 
			
		||||
	else if (dist >= m_segs[m_nsegs-1].d)
 | 
			
		||||
	{
 | 
			
		||||
		// Further than the last segment, skip.
 | 
			
		||||
		if (m_nsegs >= MAX_LOCAL_SEGS)
 | 
			
		||||
			return;
 | 
			
		||||
		// Last, trivial accept.
 | 
			
		||||
		seg = &m_segs[m_nsegs];
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// Insert inbetween.
 | 
			
		||||
		int i;
 | 
			
		||||
		for (i = 0; i < m_nsegs; ++i)
 | 
			
		||||
			if (dist <= m_segs[i].d)
 | 
			
		||||
				break;
 | 
			
		||||
		const int tgt = i+1;
 | 
			
		||||
		const int n = dtMin(m_nsegs-i, MAX_LOCAL_SEGS-tgt);
 | 
			
		||||
		dtAssert(tgt+n <= MAX_LOCAL_SEGS);
 | 
			
		||||
		if (n > 0)
 | 
			
		||||
			memmove(&m_segs[tgt], &m_segs[i], sizeof(Segment)*n);
 | 
			
		||||
		seg = &m_segs[i];
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	seg->d = dist;
 | 
			
		||||
	memcpy(seg->s, s, sizeof(float)*6);
 | 
			
		||||
	
 | 
			
		||||
	if (m_nsegs < MAX_LOCAL_SEGS)
 | 
			
		||||
		m_nsegs++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtLocalBoundary::update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
 | 
			
		||||
							 dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	static const int MAX_SEGS_PER_POLY = DT_VERTS_PER_POLYGON*3;
 | 
			
		||||
	
 | 
			
		||||
	if (!ref)
 | 
			
		||||
	{
 | 
			
		||||
		dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
 | 
			
		||||
		m_nsegs = 0;
 | 
			
		||||
		m_npolys = 0;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	dtVcopy(m_center, pos);
 | 
			
		||||
	
 | 
			
		||||
	// First query non-overlapping polygons.
 | 
			
		||||
	navquery->findLocalNeighbourhood(ref, pos, collisionQueryRange,
 | 
			
		||||
									 filter, m_polys, 0, &m_npolys, MAX_LOCAL_POLYS);
 | 
			
		||||
	
 | 
			
		||||
	// Secondly, store all polygon edges.
 | 
			
		||||
	m_nsegs = 0;
 | 
			
		||||
	float segs[MAX_SEGS_PER_POLY*6];
 | 
			
		||||
	int nsegs = 0;
 | 
			
		||||
	for (int j = 0; j < m_npolys; ++j)
 | 
			
		||||
	{
 | 
			
		||||
		navquery->getPolyWallSegments(m_polys[j], filter, segs, 0, &nsegs, MAX_SEGS_PER_POLY);
 | 
			
		||||
		for (int k = 0; k < nsegs; ++k)
 | 
			
		||||
		{
 | 
			
		||||
			const float* s = &segs[k*6];
 | 
			
		||||
			// Skip too distant segments.
 | 
			
		||||
			float tseg;
 | 
			
		||||
			const float distSqr = dtDistancePtSegSqr2D(pos, s, s+3, tseg);
 | 
			
		||||
			if (distSqr > dtSqr(collisionQueryRange))
 | 
			
		||||
				continue;
 | 
			
		||||
			addSegment(distSqr, s);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtLocalBoundary::isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	if (!m_npolys)
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	// Check that all polygons still pass query filter.
 | 
			
		||||
	for (int i = 0; i < m_npolys; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (!navquery->isValidPolyRef(m_polys[i], filter))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,619 @@
 | 
			
		||||
//
 | 
			
		||||
// 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 "DetourObstacleAvoidance.h"
 | 
			
		||||
#include "DetourCommon.h"
 | 
			
		||||
#include "DetourMath.h"
 | 
			
		||||
#include "DetourAlloc.h"
 | 
			
		||||
#include "DetourAssert.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <float.h>
 | 
			
		||||
#include <new>
 | 
			
		||||
 | 
			
		||||
static const float DT_PI = 3.14159265f;
 | 
			
		||||
 | 
			
		||||
static int sweepCircleCircle(const float* c0, const float r0, const float* v,
 | 
			
		||||
							 const float* c1, const float r1,
 | 
			
		||||
							 float& tmin, float& tmax)
 | 
			
		||||
{
 | 
			
		||||
	static const float EPS = 0.0001f;
 | 
			
		||||
	float s[3];
 | 
			
		||||
	dtVsub(s,c1,c0);
 | 
			
		||||
	float r = r0+r1;
 | 
			
		||||
	float c = dtVdot2D(s,s) - r*r;
 | 
			
		||||
	float a = dtVdot2D(v,v);
 | 
			
		||||
	if (a < EPS) return 0;	// not moving
 | 
			
		||||
	
 | 
			
		||||
	// Overlap, calc time to exit.
 | 
			
		||||
	float b = dtVdot2D(v,s);
 | 
			
		||||
	float d = b*b - a*c;
 | 
			
		||||
	if (d < 0.0f) return 0; // no intersection.
 | 
			
		||||
	a = 1.0f / a;
 | 
			
		||||
	const float rd = dtMathSqrtf(d);
 | 
			
		||||
	tmin = (b - rd) * a;
 | 
			
		||||
	tmax = (b + rd) * a;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int isectRaySeg(const float* ap, const float* u,
 | 
			
		||||
					   const float* bp, const float* bq,
 | 
			
		||||
					   float& t)
 | 
			
		||||
{
 | 
			
		||||
	float v[3], w[3];
 | 
			
		||||
	dtVsub(v,bq,bp);
 | 
			
		||||
	dtVsub(w,ap,bp);
 | 
			
		||||
	float d = dtVperp2D(u,v);
 | 
			
		||||
	if (dtMathFabsf(d) < 1e-6f) return 0;
 | 
			
		||||
	d = 1.0f/d;
 | 
			
		||||
	t = dtVperp2D(v,w) * d;
 | 
			
		||||
	if (t < 0 || t > 1) return 0;
 | 
			
		||||
	float s = dtVperp2D(u,w) * d;
 | 
			
		||||
	if (s < 0 || s > 1) return 0;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
 | 
			
		||||
{
 | 
			
		||||
	void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
 | 
			
		||||
	if (!mem) return 0;
 | 
			
		||||
	return new(mem) dtObstacleAvoidanceDebugData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
 | 
			
		||||
{
 | 
			
		||||
	if (!ptr) return;
 | 
			
		||||
	ptr->~dtObstacleAvoidanceDebugData();
 | 
			
		||||
	dtFree(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
 | 
			
		||||
	m_nsamples(0),
 | 
			
		||||
	m_maxSamples(0),
 | 
			
		||||
	m_vel(0),
 | 
			
		||||
	m_ssize(0),
 | 
			
		||||
	m_pen(0),
 | 
			
		||||
	m_vpen(0),
 | 
			
		||||
	m_vcpen(0),
 | 
			
		||||
	m_spen(0),
 | 
			
		||||
	m_tpen(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
 | 
			
		||||
{
 | 
			
		||||
	dtFree(m_vel);
 | 
			
		||||
	dtFree(m_ssize);
 | 
			
		||||
	dtFree(m_pen);
 | 
			
		||||
	dtFree(m_vpen);
 | 
			
		||||
	dtFree(m_vcpen);
 | 
			
		||||
	dtFree(m_spen);
 | 
			
		||||
	dtFree(m_tpen);
 | 
			
		||||
}
 | 
			
		||||
		
 | 
			
		||||
bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(maxSamples);
 | 
			
		||||
	m_maxSamples = maxSamples;
 | 
			
		||||
 | 
			
		||||
	m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_vel)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_pen)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_ssize)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_vpen)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_vcpen)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_spen)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_tpen)
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceDebugData::reset()
 | 
			
		||||
{
 | 
			
		||||
	m_nsamples = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
 | 
			
		||||
											 const float vpen, const float vcpen, const float spen, const float tpen)
 | 
			
		||||
{
 | 
			
		||||
	if (m_nsamples >= m_maxSamples)
 | 
			
		||||
		return;
 | 
			
		||||
	dtAssert(m_vel);
 | 
			
		||||
	dtAssert(m_ssize);
 | 
			
		||||
	dtAssert(m_pen);
 | 
			
		||||
	dtAssert(m_vpen);
 | 
			
		||||
	dtAssert(m_vcpen);
 | 
			
		||||
	dtAssert(m_spen);
 | 
			
		||||
	dtAssert(m_tpen);
 | 
			
		||||
	dtVcopy(&m_vel[m_nsamples*3], vel);
 | 
			
		||||
	m_ssize[m_nsamples] = ssize;
 | 
			
		||||
	m_pen[m_nsamples] = pen;
 | 
			
		||||
	m_vpen[m_nsamples] = vpen;
 | 
			
		||||
	m_vcpen[m_nsamples] = vcpen;
 | 
			
		||||
	m_spen[m_nsamples] = spen;
 | 
			
		||||
	m_tpen[m_nsamples] = tpen;
 | 
			
		||||
	m_nsamples++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void normalizeArray(float* arr, const int n)
 | 
			
		||||
{
 | 
			
		||||
	// Normalize penaly range.
 | 
			
		||||
	float minPen = FLT_MAX;
 | 
			
		||||
	float maxPen = -FLT_MAX;
 | 
			
		||||
	for (int i = 0; i < n; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		minPen = dtMin(minPen, arr[i]);
 | 
			
		||||
		maxPen = dtMax(maxPen, arr[i]);
 | 
			
		||||
	}
 | 
			
		||||
	const float penRange = maxPen-minPen;
 | 
			
		||||
	const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
 | 
			
		||||
	for (int i = 0; i < n; ++i)
 | 
			
		||||
		arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceDebugData::normalizeSamples()
 | 
			
		||||
{
 | 
			
		||||
	normalizeArray(m_pen, m_nsamples);
 | 
			
		||||
	normalizeArray(m_vpen, m_nsamples);
 | 
			
		||||
	normalizeArray(m_vcpen, m_nsamples);
 | 
			
		||||
	normalizeArray(m_spen, m_nsamples);
 | 
			
		||||
	normalizeArray(m_tpen, m_nsamples);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
 | 
			
		||||
{
 | 
			
		||||
	void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
 | 
			
		||||
	if (!mem) return 0;
 | 
			
		||||
	return new(mem) dtObstacleAvoidanceQuery;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
 | 
			
		||||
{
 | 
			
		||||
	if (!ptr) return;
 | 
			
		||||
	ptr->~dtObstacleAvoidanceQuery();
 | 
			
		||||
	dtFree(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
 | 
			
		||||
	m_invHorizTime(0),
 | 
			
		||||
	m_vmax(0),
 | 
			
		||||
	m_invVmax(0),
 | 
			
		||||
	m_maxCircles(0),
 | 
			
		||||
	m_circles(0),
 | 
			
		||||
	m_ncircles(0),
 | 
			
		||||
	m_maxSegments(0),
 | 
			
		||||
	m_segments(0),
 | 
			
		||||
	m_nsegments(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
 | 
			
		||||
{
 | 
			
		||||
	dtFree(m_circles);
 | 
			
		||||
	dtFree(m_segments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
 | 
			
		||||
{
 | 
			
		||||
	m_maxCircles = maxCircles;
 | 
			
		||||
	m_ncircles = 0;
 | 
			
		||||
	m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_circles)
 | 
			
		||||
		return false;
 | 
			
		||||
	memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
 | 
			
		||||
 | 
			
		||||
	m_maxSegments = maxSegments;
 | 
			
		||||
	m_nsegments = 0;
 | 
			
		||||
	m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_segments)
 | 
			
		||||
		return false;
 | 
			
		||||
	memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceQuery::reset()
 | 
			
		||||
{
 | 
			
		||||
	m_ncircles = 0;
 | 
			
		||||
	m_nsegments = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
 | 
			
		||||
										 const float* vel, const float* dvel)
 | 
			
		||||
{
 | 
			
		||||
	if (m_ncircles >= m_maxCircles)
 | 
			
		||||
		return;
 | 
			
		||||
		
 | 
			
		||||
	dtObstacleCircle* cir = &m_circles[m_ncircles++];
 | 
			
		||||
	dtVcopy(cir->p, pos);
 | 
			
		||||
	cir->rad = rad;
 | 
			
		||||
	dtVcopy(cir->vel, vel);
 | 
			
		||||
	dtVcopy(cir->dvel, dvel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
 | 
			
		||||
{
 | 
			
		||||
	if (m_nsegments >= m_maxSegments)
 | 
			
		||||
		return;
 | 
			
		||||
	
 | 
			
		||||
	dtObstacleSegment* seg = &m_segments[m_nsegments++];
 | 
			
		||||
	dtVcopy(seg->p, p);
 | 
			
		||||
	dtVcopy(seg->q, q);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
 | 
			
		||||
{
 | 
			
		||||
	// Prepare obstacles
 | 
			
		||||
	for (int i = 0; i < m_ncircles; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		dtObstacleCircle* cir = &m_circles[i];
 | 
			
		||||
		
 | 
			
		||||
		// Side
 | 
			
		||||
		const float* pa = pos;
 | 
			
		||||
		const float* pb = cir->p;
 | 
			
		||||
		
 | 
			
		||||
		const float orig[3] = {0,0,0};
 | 
			
		||||
		float dv[3];
 | 
			
		||||
		dtVsub(cir->dp,pb,pa);
 | 
			
		||||
		dtVnormalize(cir->dp);
 | 
			
		||||
		dtVsub(dv, cir->dvel, dvel);
 | 
			
		||||
		
 | 
			
		||||
		const float a = dtTriArea2D(orig, cir->dp,dv);
 | 
			
		||||
		if (a < 0.01f)
 | 
			
		||||
		{
 | 
			
		||||
			cir->np[0] = -cir->dp[2];
 | 
			
		||||
			cir->np[2] = cir->dp[0];
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			cir->np[0] = cir->dp[2];
 | 
			
		||||
			cir->np[2] = -cir->dp[0];
 | 
			
		||||
		}
 | 
			
		||||
	}	
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < m_nsegments; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		dtObstacleSegment* seg = &m_segments[i];
 | 
			
		||||
		
 | 
			
		||||
		// Precalc if the agent is really close to the segment.
 | 
			
		||||
		const float r = 0.01f;
 | 
			
		||||
		float t;
 | 
			
		||||
		seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
 | 
			
		||||
	}	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Calculate the collision penalty for a given velocity vector
 | 
			
		||||
 * 
 | 
			
		||||
 * @param vcand sampled velocity
 | 
			
		||||
 * @param dvel desired velocity
 | 
			
		||||
 * @param minPenalty threshold penalty for early out
 | 
			
		||||
 */
 | 
			
		||||
float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
 | 
			
		||||
											  const float* pos, const float rad,
 | 
			
		||||
											  const float* vel, const float* dvel,
 | 
			
		||||
											  const float minPenalty,
 | 
			
		||||
											  dtObstacleAvoidanceDebugData* debug)
 | 
			
		||||
{
 | 
			
		||||
	// penalty for straying away from the desired and current velocities
 | 
			
		||||
	const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax);
 | 
			
		||||
	const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax);
 | 
			
		||||
 | 
			
		||||
	// find the threshold hit time to bail out based on the early out penalty
 | 
			
		||||
	// (see how the penalty is calculated below to understnad)
 | 
			
		||||
	float minPen = minPenalty - vpen - vcpen;
 | 
			
		||||
	float tThresold = (m_params.weightToi / minPen - 0.1f) * m_params.horizTime;
 | 
			
		||||
	if (tThresold - m_params.horizTime > -FLT_EPSILON)
 | 
			
		||||
		return minPenalty; // already too much
 | 
			
		||||
 | 
			
		||||
	// Find min time of impact and exit amongst all obstacles.
 | 
			
		||||
	float tmin = m_params.horizTime;
 | 
			
		||||
	float side = 0;
 | 
			
		||||
	int nside = 0;
 | 
			
		||||
	
 | 
			
		||||
	for (int i = 0; i < m_ncircles; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		const dtObstacleCircle* cir = &m_circles[i];
 | 
			
		||||
			
 | 
			
		||||
		// RVO
 | 
			
		||||
		float vab[3];
 | 
			
		||||
		dtVscale(vab, vcand, 2);
 | 
			
		||||
		dtVsub(vab, vab, vel);
 | 
			
		||||
		dtVsub(vab, vab, cir->vel);
 | 
			
		||||
		
 | 
			
		||||
		// Side
 | 
			
		||||
		side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
 | 
			
		||||
		nside++;
 | 
			
		||||
		
 | 
			
		||||
		float htmin = 0, htmax = 0;
 | 
			
		||||
		if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
 | 
			
		||||
			continue;
 | 
			
		||||
		
 | 
			
		||||
		// Handle overlapping obstacles.
 | 
			
		||||
		if (htmin < 0.0f && htmax > 0.0f)
 | 
			
		||||
		{
 | 
			
		||||
			// Avoid more when overlapped.
 | 
			
		||||
			htmin = -htmin * 0.5f;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (htmin >= 0.0f)
 | 
			
		||||
		{
 | 
			
		||||
			// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
 | 
			
		||||
			if (htmin < tmin)
 | 
			
		||||
			{
 | 
			
		||||
				tmin = htmin;
 | 
			
		||||
				if (tmin < tThresold)
 | 
			
		||||
					return minPenalty;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < m_nsegments; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		const dtObstacleSegment* seg = &m_segments[i];
 | 
			
		||||
		float htmin = 0;
 | 
			
		||||
		
 | 
			
		||||
		if (seg->touch)
 | 
			
		||||
		{
 | 
			
		||||
			// Special case when the agent is very close to the segment.
 | 
			
		||||
			float sdir[3], snorm[3];
 | 
			
		||||
			dtVsub(sdir, seg->q, seg->p);
 | 
			
		||||
			snorm[0] = -sdir[2];
 | 
			
		||||
			snorm[2] = sdir[0];
 | 
			
		||||
			// If the velocity is pointing towards the segment, no collision.
 | 
			
		||||
			if (dtVdot2D(snorm, vcand) < 0.0f)
 | 
			
		||||
				continue;
 | 
			
		||||
			// Else immediate collision.
 | 
			
		||||
			htmin = 0.0f;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
 | 
			
		||||
				continue;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// Avoid less when facing walls.
 | 
			
		||||
		htmin *= 2.0f;
 | 
			
		||||
		
 | 
			
		||||
		// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
 | 
			
		||||
		if (htmin < tmin)
 | 
			
		||||
		{
 | 
			
		||||
			tmin = htmin;
 | 
			
		||||
			if (tmin < tThresold)
 | 
			
		||||
				return minPenalty;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Normalize side bias, to prevent it dominating too much.
 | 
			
		||||
	if (nside)
 | 
			
		||||
		side /= nside;
 | 
			
		||||
	
 | 
			
		||||
	const float spen = m_params.weightSide * side;
 | 
			
		||||
	const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime));
 | 
			
		||||
	
 | 
			
		||||
	const float penalty = vpen + vcpen + spen + tpen;
 | 
			
		||||
	
 | 
			
		||||
	// Store different penalties for debug viewing
 | 
			
		||||
	if (debug)
 | 
			
		||||
		debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
 | 
			
		||||
	
 | 
			
		||||
	return penalty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
 | 
			
		||||
												 const float* vel, const float* dvel, float* nvel,
 | 
			
		||||
												 const dtObstacleAvoidanceParams* params,
 | 
			
		||||
												 dtObstacleAvoidanceDebugData* debug)
 | 
			
		||||
{
 | 
			
		||||
	prepare(pos, dvel);
 | 
			
		||||
	
 | 
			
		||||
	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
 | 
			
		||||
	m_invHorizTime = 1.0f / m_params.horizTime;
 | 
			
		||||
	m_vmax = vmax;
 | 
			
		||||
	m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
 | 
			
		||||
	
 | 
			
		||||
	dtVset(nvel, 0,0,0);
 | 
			
		||||
	
 | 
			
		||||
	if (debug)
 | 
			
		||||
		debug->reset();
 | 
			
		||||
 | 
			
		||||
	const float cvx = dvel[0] * m_params.velBias;
 | 
			
		||||
	const float cvz = dvel[2] * m_params.velBias;
 | 
			
		||||
	const float cs = vmax * 2 * (1 - m_params.velBias) / (float)(m_params.gridSize-1);
 | 
			
		||||
	const float half = (m_params.gridSize-1)*cs*0.5f;
 | 
			
		||||
		
 | 
			
		||||
	float minPenalty = FLT_MAX;
 | 
			
		||||
	int ns = 0;
 | 
			
		||||
		
 | 
			
		||||
	for (int y = 0; y < m_params.gridSize; ++y)
 | 
			
		||||
	{
 | 
			
		||||
		for (int x = 0; x < m_params.gridSize; ++x)
 | 
			
		||||
		{
 | 
			
		||||
			float vcand[3];
 | 
			
		||||
			vcand[0] = cvx + x*cs - half;
 | 
			
		||||
			vcand[1] = 0;
 | 
			
		||||
			vcand[2] = cvz + y*cs - half;
 | 
			
		||||
			
 | 
			
		||||
			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
 | 
			
		||||
			
 | 
			
		||||
			const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, minPenalty, debug);
 | 
			
		||||
			ns++;
 | 
			
		||||
			if (penalty < minPenalty)
 | 
			
		||||
			{
 | 
			
		||||
				minPenalty = penalty;
 | 
			
		||||
				dtVcopy(nvel, vcand);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return ns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// vector normalization that ignores the y-component.
 | 
			
		||||
inline void dtNormalize2D(float* v)
 | 
			
		||||
{
 | 
			
		||||
	float d = dtMathSqrtf(v[0] * v[0] + v[2] * v[2]);
 | 
			
		||||
	if (d==0)
 | 
			
		||||
		return;
 | 
			
		||||
	d = 1.0f / d;
 | 
			
		||||
	v[0] *= d;
 | 
			
		||||
	v[2] *= d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// vector normalization that ignores the y-component.
 | 
			
		||||
inline void dtRorate2D(float* dest, const float* v, float ang)
 | 
			
		||||
{
 | 
			
		||||
	float c = cosf(ang);
 | 
			
		||||
	float s = sinf(ang);
 | 
			
		||||
	dest[0] = v[0]*c - v[2]*s;
 | 
			
		||||
	dest[2] = v[0]*s + v[2]*c;
 | 
			
		||||
	dest[1] = v[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
 | 
			
		||||
													 const float* vel, const float* dvel, float* nvel,
 | 
			
		||||
													 const dtObstacleAvoidanceParams* params,
 | 
			
		||||
													 dtObstacleAvoidanceDebugData* debug)
 | 
			
		||||
{
 | 
			
		||||
	prepare(pos, dvel);
 | 
			
		||||
	
 | 
			
		||||
	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
 | 
			
		||||
	m_invHorizTime = 1.0f / m_params.horizTime;
 | 
			
		||||
	m_vmax = vmax;
 | 
			
		||||
	m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
 | 
			
		||||
	
 | 
			
		||||
	dtVset(nvel, 0,0,0);
 | 
			
		||||
	
 | 
			
		||||
	if (debug)
 | 
			
		||||
		debug->reset();
 | 
			
		||||
 | 
			
		||||
	// Build sampling pattern aligned to desired velocity.
 | 
			
		||||
	float pat[(DT_MAX_PATTERN_DIVS*DT_MAX_PATTERN_RINGS+1)*2];
 | 
			
		||||
	int npat = 0;
 | 
			
		||||
 | 
			
		||||
	const int ndivs = (int)m_params.adaptiveDivs;
 | 
			
		||||
	const int nrings= (int)m_params.adaptiveRings;
 | 
			
		||||
	const int depth = (int)m_params.adaptiveDepth;
 | 
			
		||||
	
 | 
			
		||||
	const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS);
 | 
			
		||||
	const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS);
 | 
			
		||||
	const float da = (1.0f/nd) * DT_PI*2;
 | 
			
		||||
	const float ca = cosf(da);
 | 
			
		||||
	const float sa = sinf(da);
 | 
			
		||||
 | 
			
		||||
	// desired direction
 | 
			
		||||
	float ddir[6];
 | 
			
		||||
	dtVcopy(ddir, dvel);
 | 
			
		||||
	dtNormalize2D(ddir);
 | 
			
		||||
	dtRorate2D (ddir+3, ddir, da*0.5f); // rotated by da/2
 | 
			
		||||
 | 
			
		||||
	// Always add sample at zero
 | 
			
		||||
	pat[npat*2+0] = 0;
 | 
			
		||||
	pat[npat*2+1] = 0;
 | 
			
		||||
	npat++;
 | 
			
		||||
	
 | 
			
		||||
	for (int j = 0; j < nr; ++j)
 | 
			
		||||
	{
 | 
			
		||||
		const float r = (float)(nr-j)/(float)nr;
 | 
			
		||||
		pat[npat*2+0] = ddir[(j%2)*3] * r;
 | 
			
		||||
		pat[npat*2+1] = ddir[(j%2)*3+2] * r;
 | 
			
		||||
		float* last1 = pat + npat*2;
 | 
			
		||||
		float* last2 = last1;
 | 
			
		||||
		npat++;
 | 
			
		||||
 | 
			
		||||
		for (int i = 1; i < nd-1; i+=2)
 | 
			
		||||
		{
 | 
			
		||||
			// get next point on the "right" (rotate CW)
 | 
			
		||||
			pat[npat*2+0] = last1[0]*ca + last1[1]*sa;
 | 
			
		||||
			pat[npat*2+1] = -last1[0]*sa + last1[1]*ca;
 | 
			
		||||
			// get next point on the "left" (rotate CCW)
 | 
			
		||||
			pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
 | 
			
		||||
			pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
 | 
			
		||||
 | 
			
		||||
			last1 = pat + npat*2;
 | 
			
		||||
			last2 = last1 + 2;
 | 
			
		||||
			npat += 2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((nd&1) == 0)
 | 
			
		||||
		{
 | 
			
		||||
			pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
 | 
			
		||||
			pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
 | 
			
		||||
			npat++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Start sampling.
 | 
			
		||||
	float cr = vmax * (1.0f - m_params.velBias);
 | 
			
		||||
	float res[3];
 | 
			
		||||
	dtVset(res, dvel[0] * m_params.velBias, 0, dvel[2] * m_params.velBias);
 | 
			
		||||
	int ns = 0;
 | 
			
		||||
 | 
			
		||||
	for (int k = 0; k < depth; ++k)
 | 
			
		||||
	{
 | 
			
		||||
		float minPenalty = FLT_MAX;
 | 
			
		||||
		float bvel[3];
 | 
			
		||||
		dtVset(bvel, 0,0,0);
 | 
			
		||||
		
 | 
			
		||||
		for (int i = 0; i < npat; ++i)
 | 
			
		||||
		{
 | 
			
		||||
			float vcand[3];
 | 
			
		||||
			vcand[0] = res[0] + pat[i*2+0]*cr;
 | 
			
		||||
			vcand[1] = 0;
 | 
			
		||||
			vcand[2] = res[2] + pat[i*2+1]*cr;
 | 
			
		||||
			
 | 
			
		||||
			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
 | 
			
		||||
			
 | 
			
		||||
			const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, minPenalty, debug);
 | 
			
		||||
			ns++;
 | 
			
		||||
			if (penalty < minPenalty)
 | 
			
		||||
			{
 | 
			
		||||
				minPenalty = penalty;
 | 
			
		||||
				dtVcopy(bvel, vcand);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dtVcopy(res, bvel);
 | 
			
		||||
 | 
			
		||||
		cr *= 0.5f;
 | 
			
		||||
	}	
 | 
			
		||||
	
 | 
			
		||||
	dtVcopy(nvel, res);
 | 
			
		||||
	
 | 
			
		||||
	return ns;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,597 @@
 | 
			
		||||
//
 | 
			
		||||
// 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 <string.h>
 | 
			
		||||
#include "DetourPathCorridor.h"
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
#include "DetourCommon.h"
 | 
			
		||||
#include "DetourAssert.h"
 | 
			
		||||
#include "DetourAlloc.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
							  const dtPolyRef* visited, const int nvisited)
 | 
			
		||||
{
 | 
			
		||||
	int furthestPath = -1;
 | 
			
		||||
	int furthestVisited = -1;
 | 
			
		||||
	
 | 
			
		||||
	// Find furthest common polygon.
 | 
			
		||||
	for (int i = npath-1; i >= 0; --i)
 | 
			
		||||
	{
 | 
			
		||||
		bool found = false;
 | 
			
		||||
		for (int j = nvisited-1; j >= 0; --j)
 | 
			
		||||
		{
 | 
			
		||||
			if (path[i] == visited[j])
 | 
			
		||||
			{
 | 
			
		||||
				furthestPath = i;
 | 
			
		||||
				furthestVisited = j;
 | 
			
		||||
				found = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (found)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// If no intersection found just return current path. 
 | 
			
		||||
	if (furthestPath == -1 || furthestVisited == -1)
 | 
			
		||||
		return npath;
 | 
			
		||||
	
 | 
			
		||||
	// Concatenate paths.	
 | 
			
		||||
	
 | 
			
		||||
	// Adjust beginning of the buffer to include the visited.
 | 
			
		||||
	const int req = nvisited - furthestVisited;
 | 
			
		||||
	const int orig = dtMin(furthestPath+1, npath);
 | 
			
		||||
	int size = dtMax(0, npath-orig);
 | 
			
		||||
	if (req+size > maxPath)
 | 
			
		||||
		size = maxPath-req;
 | 
			
		||||
	if (size)
 | 
			
		||||
		memmove(path+req, path+orig, size*sizeof(dtPolyRef));
 | 
			
		||||
	
 | 
			
		||||
	// Store visited
 | 
			
		||||
	for (int i = 0; i < req; ++i)
 | 
			
		||||
		path[i] = visited[(nvisited-1)-i];				
 | 
			
		||||
	
 | 
			
		||||
	return req+size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
							const dtPolyRef* visited, const int nvisited)
 | 
			
		||||
{
 | 
			
		||||
	int furthestPath = -1;
 | 
			
		||||
	int furthestVisited = -1;
 | 
			
		||||
	
 | 
			
		||||
	// Find furthest common polygon.
 | 
			
		||||
	for (int i = 0; i < npath; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		bool found = false;
 | 
			
		||||
		for (int j = nvisited-1; j >= 0; --j)
 | 
			
		||||
		{
 | 
			
		||||
			if (path[i] == visited[j])
 | 
			
		||||
			{
 | 
			
		||||
				furthestPath = i;
 | 
			
		||||
				furthestVisited = j;
 | 
			
		||||
				found = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (found)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// If no intersection found just return current path. 
 | 
			
		||||
	if (furthestPath == -1 || furthestVisited == -1)
 | 
			
		||||
		return npath;
 | 
			
		||||
	
 | 
			
		||||
	// Concatenate paths.
 | 
			
		||||
	const int ppos = furthestPath+1;
 | 
			
		||||
	const int vpos = furthestVisited+1;
 | 
			
		||||
	const int count = dtMin(nvisited-vpos, maxPath-ppos);
 | 
			
		||||
	dtAssert(ppos+count <= maxPath);
 | 
			
		||||
	if (count)
 | 
			
		||||
		memcpy(path+ppos, visited+vpos, sizeof(dtPolyRef)*count);
 | 
			
		||||
	
 | 
			
		||||
	return ppos+count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
 | 
			
		||||
								 const dtPolyRef* visited, const int nvisited)
 | 
			
		||||
{
 | 
			
		||||
	int furthestPath = -1;
 | 
			
		||||
	int furthestVisited = -1;
 | 
			
		||||
	
 | 
			
		||||
	// Find furthest common polygon.
 | 
			
		||||
	for (int i = npath-1; i >= 0; --i)
 | 
			
		||||
	{
 | 
			
		||||
		bool found = false;
 | 
			
		||||
		for (int j = nvisited-1; j >= 0; --j)
 | 
			
		||||
		{
 | 
			
		||||
			if (path[i] == visited[j])
 | 
			
		||||
			{
 | 
			
		||||
				furthestPath = i;
 | 
			
		||||
				furthestVisited = j;
 | 
			
		||||
				found = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (found)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// If no intersection found just return current path. 
 | 
			
		||||
	if (furthestPath == -1 || furthestVisited == -1)
 | 
			
		||||
		return npath;
 | 
			
		||||
	
 | 
			
		||||
	// Concatenate paths.	
 | 
			
		||||
	
 | 
			
		||||
	// Adjust beginning of the buffer to include the visited.
 | 
			
		||||
	const int req = furthestVisited;
 | 
			
		||||
	if (req <= 0)
 | 
			
		||||
		return npath;
 | 
			
		||||
	
 | 
			
		||||
	const int orig = furthestPath;
 | 
			
		||||
	int size = dtMax(0, npath-orig);
 | 
			
		||||
	if (req+size > maxPath)
 | 
			
		||||
		size = maxPath-req;
 | 
			
		||||
	if (size)
 | 
			
		||||
		memmove(path+req, path+orig, size*sizeof(dtPolyRef));
 | 
			
		||||
	
 | 
			
		||||
	// Store visited
 | 
			
		||||
	for (int i = 0; i < req; ++i)
 | 
			
		||||
		path[i] = visited[i];
 | 
			
		||||
	
 | 
			
		||||
	return req+size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@class dtPathCorridor
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
The corridor is loaded with a path, usually obtained from a #dtNavMeshQuery::findPath() query. The corridor
 | 
			
		||||
is then used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate 
 | 
			
		||||
agent locomotion.
 | 
			
		||||
 | 
			
		||||
Example of a common use case:
 | 
			
		||||
 | 
			
		||||
-# Construct the corridor object and call #init() to allocate its path buffer.
 | 
			
		||||
-# Obtain a path from a #dtNavMeshQuery object.
 | 
			
		||||
-# Use #reset() to set the agent's current position. (At the beginning of the path.)
 | 
			
		||||
-# Use #setCorridor() to load the path and target.
 | 
			
		||||
-# Use #findCorners() to plan movement. (This handles dynamic path straightening.)
 | 
			
		||||
-# Use #movePosition() to feed agent movement back into the corridor. (The corridor will automatically adjust as needed.)
 | 
			
		||||
-# If the target is moving, use #moveTargetPosition() to update the end of the corridor. 
 | 
			
		||||
   (The corridor will automatically adjust as needed.)
 | 
			
		||||
-# Repeat the previous 3 steps to continue to move the agent.
 | 
			
		||||
 | 
			
		||||
The corridor position and target are always constrained to the navigation mesh.
 | 
			
		||||
 | 
			
		||||
One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local 
 | 
			
		||||
steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path. 
 | 
			
		||||
This class uses local mesh queries to detect and update the corridor as needed to handle these types of issues. 
 | 
			
		||||
 | 
			
		||||
The fact that local mesh queries are used to move the position and target locations results in two beahviors that 
 | 
			
		||||
need to be considered:
 | 
			
		||||
 | 
			
		||||
Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further 
 | 
			
		||||
the target is moved from its original location, and the further the position is moved outside the original corridor, 
 | 
			
		||||
the more likely the path will become non-optimal. This issue can be addressed by periodically running the 
 | 
			
		||||
#optimizePathTopology() and #optimizePathVisibility() methods.
 | 
			
		||||
 | 
			
		||||
All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most accurate 
 | 
			
		||||
use case is to move the position and target in small increments. If a large increment is used, then the corridor 
 | 
			
		||||
may not be able to accurately find the new location.  Because of this limiation, if a position is moved in a large
 | 
			
		||||
increment, then compare the desired and resulting polygon references. If the two do not match, then path replanning 
 | 
			
		||||
may be needed.  E.g. If you move the target, check #getLastPoly() to see if it is the expected polygon.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
dtPathCorridor::dtPathCorridor() :
 | 
			
		||||
	m_path(0),
 | 
			
		||||
	m_npath(0),
 | 
			
		||||
	m_maxPath(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtPathCorridor::~dtPathCorridor()
 | 
			
		||||
{
 | 
			
		||||
	dtFree(m_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @par
 | 
			
		||||
///
 | 
			
		||||
/// @warning Cannot be called more than once.
 | 
			
		||||
bool dtPathCorridor::init(const int maxPath)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(!m_path);
 | 
			
		||||
	m_path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*maxPath, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_path)
 | 
			
		||||
		return false;
 | 
			
		||||
	m_npath = 0;
 | 
			
		||||
	m_maxPath = maxPath;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @par
 | 
			
		||||
///
 | 
			
		||||
/// Essentially, the corridor is set of one polygon in size with the target
 | 
			
		||||
/// equal to the position.
 | 
			
		||||
void dtPathCorridor::reset(dtPolyRef ref, const float* pos)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtVcopy(m_pos, pos);
 | 
			
		||||
	dtVcopy(m_target, pos);
 | 
			
		||||
	m_path[0] = ref;
 | 
			
		||||
	m_npath = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
This is the function used to plan local movement within the corridor. One or more corners can be 
 | 
			
		||||
detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath.
 | 
			
		||||
 | 
			
		||||
Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1) 
 | 
			
		||||
For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners. 
 | 
			
		||||
So if 10 corners are needed, the buffers should be sized for 11 corners.
 | 
			
		||||
 | 
			
		||||
If the target is within range, it will be the last corner and have a polygon reference id of zero.
 | 
			
		||||
*/
 | 
			
		||||
int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
 | 
			
		||||
							  dtPolyRef* cornerPolys, const int maxCorners,
 | 
			
		||||
							  dtNavMeshQuery* navquery, const dtQueryFilter* /*filter*/)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtAssert(m_npath);
 | 
			
		||||
	
 | 
			
		||||
	static const float MIN_TARGET_DIST = 0.01f;
 | 
			
		||||
	
 | 
			
		||||
	int ncorners = 0;
 | 
			
		||||
	navquery->findStraightPath(m_pos, m_target, m_path, m_npath,
 | 
			
		||||
							   cornerVerts, cornerFlags, cornerPolys, &ncorners, maxCorners);
 | 
			
		||||
	
 | 
			
		||||
	// Prune points in the beginning of the path which are too close.
 | 
			
		||||
	while (ncorners)
 | 
			
		||||
	{
 | 
			
		||||
		if ((cornerFlags[0] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
 | 
			
		||||
			dtVdist2DSqr(&cornerVerts[0], m_pos) > dtSqr(MIN_TARGET_DIST))
 | 
			
		||||
			break;
 | 
			
		||||
		ncorners--;
 | 
			
		||||
		if (ncorners)
 | 
			
		||||
		{
 | 
			
		||||
			memmove(cornerFlags, cornerFlags+1, sizeof(unsigned char)*ncorners);
 | 
			
		||||
			memmove(cornerPolys, cornerPolys+1, sizeof(dtPolyRef)*ncorners);
 | 
			
		||||
			memmove(cornerVerts, cornerVerts+3, sizeof(float)*3*ncorners);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Prune points after an off-mesh connection.
 | 
			
		||||
	for (int i = 0; i < ncorners; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (cornerFlags[i] & DT_STRAIGHTPATH_OFFMESH_CONNECTION)
 | 
			
		||||
		{
 | 
			
		||||
			ncorners = i+1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return ncorners;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Inaccurate locomotion or dynamic obstacle avoidance can force the argent position significantly outside the 
 | 
			
		||||
original corridor. Over time this can result in the formation of a non-optimal corridor. Non-optimal paths can 
 | 
			
		||||
also form near the corners of tiles.
 | 
			
		||||
 | 
			
		||||
This function uses an efficient local visibility search to try to optimize the corridor 
 | 
			
		||||
between the current position and @p next.
 | 
			
		||||
 | 
			
		||||
The corridor will change only if @p next is visible from the current position and moving directly toward the point 
 | 
			
		||||
is better than following the existing path.
 | 
			
		||||
 | 
			
		||||
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency 
 | 
			
		||||
of the call to match the needs to the agent.
 | 
			
		||||
 | 
			
		||||
This function is not suitable for long distance searches.
 | 
			
		||||
*/
 | 
			
		||||
void dtPathCorridor::optimizePathVisibility(const float* next, const float pathOptimizationRange,
 | 
			
		||||
										  dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	
 | 
			
		||||
	// Clamp the ray to max distance.
 | 
			
		||||
	float goal[3];
 | 
			
		||||
	dtVcopy(goal, next);
 | 
			
		||||
	float dist = dtVdist2D(m_pos, goal);
 | 
			
		||||
	
 | 
			
		||||
	// If too close to the goal, do not try to optimize.
 | 
			
		||||
	if (dist < 0.01f)
 | 
			
		||||
		return;
 | 
			
		||||
	
 | 
			
		||||
	// Overshoot a little. This helps to optimize open fields in tiled meshes.
 | 
			
		||||
	dist = dtMin(dist+0.01f, pathOptimizationRange);
 | 
			
		||||
	
 | 
			
		||||
	// Adjust ray length.
 | 
			
		||||
	float delta[3];
 | 
			
		||||
	dtVsub(delta, goal, m_pos);
 | 
			
		||||
	dtVmad(goal, m_pos, delta, pathOptimizationRange/dist);
 | 
			
		||||
	
 | 
			
		||||
	static const int MAX_RES = 32;
 | 
			
		||||
	dtPolyRef res[MAX_RES];
 | 
			
		||||
	float t, norm[3];
 | 
			
		||||
	int nres = 0;
 | 
			
		||||
	navquery->raycast(m_path[0], m_pos, goal, filter, &t, norm, res, &nres, MAX_RES);
 | 
			
		||||
	if (nres > 1 && t > 0.99f)
 | 
			
		||||
	{
 | 
			
		||||
		m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the 
 | 
			
		||||
original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a 
 | 
			
		||||
local area path search to try to re-optimize the corridor.
 | 
			
		||||
 | 
			
		||||
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of 
 | 
			
		||||
the call to match the needs to the agent.
 | 
			
		||||
*/
 | 
			
		||||
bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(navquery);
 | 
			
		||||
	dtAssert(filter);
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	
 | 
			
		||||
	if (m_npath < 3)
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	static const int MAX_ITER = 32;
 | 
			
		||||
	static const int MAX_RES = 32;
 | 
			
		||||
	
 | 
			
		||||
	dtPolyRef res[MAX_RES];
 | 
			
		||||
	int nres = 0;
 | 
			
		||||
	navquery->initSlicedFindPath(m_path[0], m_path[m_npath-1], m_pos, m_target, filter);
 | 
			
		||||
	navquery->updateSlicedFindPath(MAX_ITER, 0);
 | 
			
		||||
	dtStatus status = navquery->finalizeSlicedFindPathPartial(m_path, m_npath, res, &nres, MAX_RES);
 | 
			
		||||
	
 | 
			
		||||
	if (dtStatusSucceed(status) && nres > 0)
 | 
			
		||||
	{
 | 
			
		||||
		m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtPathCorridor::moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
 | 
			
		||||
											   float* startPos, float* endPos,
 | 
			
		||||
											   dtNavMeshQuery* navquery)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(navquery);
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtAssert(m_npath);
 | 
			
		||||
 | 
			
		||||
	// Advance the path up to and over the off-mesh connection.
 | 
			
		||||
	dtPolyRef prevRef = 0, polyRef = m_path[0];
 | 
			
		||||
	int npos = 0;
 | 
			
		||||
	while (npos < m_npath && polyRef != offMeshConRef)
 | 
			
		||||
	{
 | 
			
		||||
		prevRef = polyRef;
 | 
			
		||||
		polyRef = m_path[npos];
 | 
			
		||||
		npos++;
 | 
			
		||||
	}
 | 
			
		||||
	if (npos == m_npath)
 | 
			
		||||
	{
 | 
			
		||||
		// Could not find offMeshConRef
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Prune path
 | 
			
		||||
	for (int i = npos; i < m_npath; ++i)
 | 
			
		||||
		m_path[i-npos] = m_path[i];
 | 
			
		||||
	m_npath -= npos;
 | 
			
		||||
 | 
			
		||||
	refs[0] = prevRef;
 | 
			
		||||
	refs[1] = polyRef;
 | 
			
		||||
	
 | 
			
		||||
	const dtNavMesh* nav = navquery->getAttachedNavMesh();
 | 
			
		||||
	dtAssert(nav);
 | 
			
		||||
 | 
			
		||||
	dtStatus status = nav->getOffMeshConnectionPolyEndPoints(refs[0], refs[1], startPos, endPos);
 | 
			
		||||
	if (dtStatusSucceed(status))
 | 
			
		||||
	{
 | 
			
		||||
		dtVcopy(m_pos, endPos);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Behavior:
 | 
			
		||||
 | 
			
		||||
- The movement is constrained to the surface of the navigation mesh. 
 | 
			
		||||
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. 
 | 
			
		||||
- The new position will be located in the adjusted corridor's first polygon.
 | 
			
		||||
 | 
			
		||||
The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near' 
 | 
			
		||||
depends on local polygon density, query search half extents, etc.
 | 
			
		||||
 | 
			
		||||
The resulting position will differ from the desired position if the desired position is not on the navigation mesh, 
 | 
			
		||||
or it can't be reached using a local search.
 | 
			
		||||
*/
 | 
			
		||||
bool dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtAssert(m_npath);
 | 
			
		||||
	
 | 
			
		||||
	// Move along navmesh and update new position.
 | 
			
		||||
	float result[3];
 | 
			
		||||
	static const int MAX_VISITED = 16;
 | 
			
		||||
	dtPolyRef visited[MAX_VISITED];
 | 
			
		||||
	int nvisited = 0;
 | 
			
		||||
	dtStatus status = navquery->moveAlongSurface(m_path[0], m_pos, npos, filter,
 | 
			
		||||
												 result, visited, &nvisited, MAX_VISITED);
 | 
			
		||||
	if (dtStatusSucceed(status)) {
 | 
			
		||||
		m_npath = dtMergeCorridorStartMoved(m_path, m_npath, m_maxPath, visited, nvisited);
 | 
			
		||||
		
 | 
			
		||||
		// Adjust the position to stay on top of the navmesh.
 | 
			
		||||
		float h = m_pos[1];
 | 
			
		||||
		navquery->getPolyHeight(m_path[0], result, &h);
 | 
			
		||||
		result[1] = h;
 | 
			
		||||
		dtVcopy(m_pos, result);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@par
 | 
			
		||||
 | 
			
		||||
Behavior:
 | 
			
		||||
 | 
			
		||||
- The movement is constrained to the surface of the navigation mesh. 
 | 
			
		||||
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. 
 | 
			
		||||
- The new target will be located in the adjusted corridor's last polygon.
 | 
			
		||||
 | 
			
		||||
The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
 | 
			
		||||
 | 
			
		||||
The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
 | 
			
		||||
*/
 | 
			
		||||
bool dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtAssert(m_npath);
 | 
			
		||||
	
 | 
			
		||||
	// Move along navmesh and update new position.
 | 
			
		||||
	float result[3];
 | 
			
		||||
	static const int MAX_VISITED = 16;
 | 
			
		||||
	dtPolyRef visited[MAX_VISITED];
 | 
			
		||||
	int nvisited = 0;
 | 
			
		||||
	dtStatus status = navquery->moveAlongSurface(m_path[m_npath-1], m_target, npos, filter,
 | 
			
		||||
												 result, visited, &nvisited, MAX_VISITED);
 | 
			
		||||
	if (dtStatusSucceed(status))
 | 
			
		||||
	{
 | 
			
		||||
		m_npath = dtMergeCorridorEndMoved(m_path, m_npath, m_maxPath, visited, nvisited);
 | 
			
		||||
		// TODO: should we do that?
 | 
			
		||||
		// Adjust the position to stay on top of the navmesh.
 | 
			
		||||
		/*	float h = m_target[1];
 | 
			
		||||
		 navquery->getPolyHeight(m_path[m_npath-1], result, &h);
 | 
			
		||||
		 result[1] = h;*/
 | 
			
		||||
		
 | 
			
		||||
		dtVcopy(m_target, result);
 | 
			
		||||
		
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @par
 | 
			
		||||
///
 | 
			
		||||
/// The current corridor position is expected to be within the first polygon in the path. The target 
 | 
			
		||||
/// is expected to be in the last polygon. 
 | 
			
		||||
/// 
 | 
			
		||||
/// @warning The size of the path must not exceed the size of corridor's path buffer set during #init().
 | 
			
		||||
void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, const int npath)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	dtAssert(npath > 0);
 | 
			
		||||
	dtAssert(npath < m_maxPath);
 | 
			
		||||
	
 | 
			
		||||
	dtVcopy(m_target, target);
 | 
			
		||||
	memcpy(m_path, path, sizeof(dtPolyRef)*npath);
 | 
			
		||||
	m_npath = npath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtPathCorridor::fixPathStart(dtPolyRef safeRef, const float* safePos)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
 | 
			
		||||
	dtVcopy(m_pos, safePos);
 | 
			
		||||
	if (m_npath < 3 && m_npath > 0)
 | 
			
		||||
	{
 | 
			
		||||
		m_path[2] = m_path[m_npath-1];
 | 
			
		||||
		m_path[0] = safeRef;
 | 
			
		||||
		m_path[1] = 0;
 | 
			
		||||
		m_npath = 3;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		m_path[0] = safeRef;
 | 
			
		||||
		m_path[1] = 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtPathCorridor::trimInvalidPath(dtPolyRef safeRef, const float* safePos,
 | 
			
		||||
									 dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(navquery);
 | 
			
		||||
	dtAssert(filter);
 | 
			
		||||
	dtAssert(m_path);
 | 
			
		||||
	
 | 
			
		||||
	// Keep valid path as far as possible.
 | 
			
		||||
	int n = 0;
 | 
			
		||||
	while (n < m_npath && navquery->isValidPolyRef(m_path[n], filter)) {
 | 
			
		||||
		n++;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (n == m_npath)
 | 
			
		||||
	{
 | 
			
		||||
		// All valid, no need to fix.
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	else if (n == 0)
 | 
			
		||||
	{
 | 
			
		||||
		// The first polyref is bad, use current safe values.
 | 
			
		||||
		dtVcopy(m_pos, safePos);
 | 
			
		||||
		m_path[0] = safeRef;
 | 
			
		||||
		m_npath = 1;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// The path is partially usable.
 | 
			
		||||
		m_npath = n;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Clamp target pos to last poly
 | 
			
		||||
	float tgt[3];
 | 
			
		||||
	dtVcopy(tgt, m_target);
 | 
			
		||||
	navquery->closestPointOnPolyBoundary(m_path[m_npath-1], tgt, m_target);
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @par
 | 
			
		||||
///
 | 
			
		||||
/// The path can be invalidated if there are structural changes to the underlying navigation mesh, or the state of 
 | 
			
		||||
/// a polygon within the path changes resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.)
 | 
			
		||||
bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	// Check that all polygons still pass query filter.
 | 
			
		||||
	const int n = dtMin(m_npath, maxLookAhead);
 | 
			
		||||
	for (int i = 0; i < n; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (!navquery->isValidPolyRef(m_path[i], filter))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,200 @@
 | 
			
		||||
//
 | 
			
		||||
// 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 <string.h>
 | 
			
		||||
#include "DetourPathQueue.h"
 | 
			
		||||
#include "DetourNavMesh.h"
 | 
			
		||||
#include "DetourNavMeshQuery.h"
 | 
			
		||||
#include "DetourAlloc.h"
 | 
			
		||||
#include "DetourCommon.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtPathQueue::dtPathQueue() :
 | 
			
		||||
	m_nextHandle(1),
 | 
			
		||||
	m_maxPathSize(0),
 | 
			
		||||
	m_queueHead(0),
 | 
			
		||||
	m_navquery(0)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
		m_queue[i].path = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtPathQueue::~dtPathQueue()
 | 
			
		||||
{
 | 
			
		||||
	purge();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtPathQueue::purge()
 | 
			
		||||
{
 | 
			
		||||
	dtFreeNavMeshQuery(m_navquery);
 | 
			
		||||
	m_navquery = 0;
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		dtFree(m_queue[i].path);
 | 
			
		||||
		m_queue[i].path = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtPathQueue::init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav)
 | 
			
		||||
{
 | 
			
		||||
	purge();
 | 
			
		||||
 | 
			
		||||
	m_navquery = dtAllocNavMeshQuery();
 | 
			
		||||
	if (!m_navquery)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (dtStatusFailed(m_navquery->init(nav, maxSearchNodeCount)))
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	m_maxPathSize = maxPathSize;
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		m_queue[i].ref = DT_PATHQ_INVALID;
 | 
			
		||||
		m_queue[i].path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*m_maxPathSize, DT_ALLOC_PERM);
 | 
			
		||||
		if (!m_queue[i].path)
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	m_queueHead = 0;
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtPathQueue::update(const int maxIters)
 | 
			
		||||
{
 | 
			
		||||
	static const int MAX_KEEP_ALIVE = 2; // in update ticks.
 | 
			
		||||
 | 
			
		||||
	// Update path request until there is nothing to update
 | 
			
		||||
	// or upto maxIters pathfinder iterations has been consumed.
 | 
			
		||||
	int iterCount = maxIters;
 | 
			
		||||
	
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		PathQuery& q = m_queue[m_queueHead % MAX_QUEUE];
 | 
			
		||||
		
 | 
			
		||||
		// Skip inactive requests.
 | 
			
		||||
		if (q.ref == DT_PATHQ_INVALID)
 | 
			
		||||
		{
 | 
			
		||||
			m_queueHead++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// Handle completed request.
 | 
			
		||||
		if (dtStatusSucceed(q.status) || dtStatusFailed(q.status))
 | 
			
		||||
		{
 | 
			
		||||
			// If the path result has not been read in few frames, free the slot.
 | 
			
		||||
			q.keepAlive++;
 | 
			
		||||
			if (q.keepAlive > MAX_KEEP_ALIVE)
 | 
			
		||||
			{
 | 
			
		||||
				q.ref = DT_PATHQ_INVALID;
 | 
			
		||||
				q.status = 0;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			m_queueHead++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// Handle query start.
 | 
			
		||||
		if (q.status == 0)
 | 
			
		||||
		{
 | 
			
		||||
			q.status = m_navquery->initSlicedFindPath(q.startRef, q.endRef, q.startPos, q.endPos, q.filter);
 | 
			
		||||
		}		
 | 
			
		||||
		// Handle query in progress.
 | 
			
		||||
		if (dtStatusInProgress(q.status))
 | 
			
		||||
		{
 | 
			
		||||
			int iters = 0;
 | 
			
		||||
			q.status = m_navquery->updateSlicedFindPath(iterCount, &iters);
 | 
			
		||||
			iterCount -= iters;
 | 
			
		||||
		}
 | 
			
		||||
		if (dtStatusSucceed(q.status))
 | 
			
		||||
		{
 | 
			
		||||
			q.status = m_navquery->finalizeSlicedFindPath(q.path, &q.npath, m_maxPathSize);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (iterCount <= 0)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		m_queueHead++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtPathQueueRef dtPathQueue::request(dtPolyRef startRef, dtPolyRef endRef,
 | 
			
		||||
									const float* startPos, const float* endPos,
 | 
			
		||||
									const dtQueryFilter* filter)
 | 
			
		||||
{
 | 
			
		||||
	// Find empty slot
 | 
			
		||||
	int slot = -1;
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (m_queue[i].ref == DT_PATHQ_INVALID)
 | 
			
		||||
		{
 | 
			
		||||
			slot = i;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Could not find slot.
 | 
			
		||||
	if (slot == -1)
 | 
			
		||||
		return DT_PATHQ_INVALID;
 | 
			
		||||
	
 | 
			
		||||
	dtPathQueueRef ref = m_nextHandle++;
 | 
			
		||||
	if (m_nextHandle == DT_PATHQ_INVALID) m_nextHandle++;
 | 
			
		||||
	
 | 
			
		||||
	PathQuery& q = m_queue[slot];
 | 
			
		||||
	q.ref = ref;
 | 
			
		||||
	dtVcopy(q.startPos, startPos);
 | 
			
		||||
	q.startRef = startRef;
 | 
			
		||||
	dtVcopy(q.endPos, endPos);
 | 
			
		||||
	q.endRef = endRef;
 | 
			
		||||
	
 | 
			
		||||
	q.status = 0;
 | 
			
		||||
	q.npath = 0;
 | 
			
		||||
	q.filter = filter;
 | 
			
		||||
	q.keepAlive = 0;
 | 
			
		||||
	
 | 
			
		||||
	return ref;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtStatus dtPathQueue::getRequestStatus(dtPathQueueRef ref) const
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (m_queue[i].ref == ref)
 | 
			
		||||
			return m_queue[i].status;
 | 
			
		||||
	}
 | 
			
		||||
	return DT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtStatus dtPathQueue::getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < MAX_QUEUE; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		if (m_queue[i].ref == ref)
 | 
			
		||||
		{
 | 
			
		||||
			PathQuery& q = m_queue[i];
 | 
			
		||||
			dtStatus details = q.status & DT_STATUS_DETAIL_MASK;
 | 
			
		||||
			// Free request for reuse.
 | 
			
		||||
			q.ref = DT_PATHQ_INVALID;
 | 
			
		||||
			q.status = 0;
 | 
			
		||||
			// Copy path
 | 
			
		||||
			int n = dtMin(q.npath, maxPath);
 | 
			
		||||
			memcpy(path, q.path, sizeof(dtPolyRef)*n);
 | 
			
		||||
			*pathSize = n;
 | 
			
		||||
			return details | DT_SUCCESS;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return DT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,194 @@
 | 
			
		||||
//
 | 
			
		||||
// 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 <string.h>
 | 
			
		||||
#include <new>
 | 
			
		||||
#include "DetourProximityGrid.h"
 | 
			
		||||
#include "DetourCommon.h"
 | 
			
		||||
#include "DetourMath.h"
 | 
			
		||||
#include "DetourAlloc.h"
 | 
			
		||||
#include "DetourAssert.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtProximityGrid* dtAllocProximityGrid()
 | 
			
		||||
{
 | 
			
		||||
	void* mem = dtAlloc(sizeof(dtProximityGrid), DT_ALLOC_PERM);
 | 
			
		||||
	if (!mem) return 0;
 | 
			
		||||
	return new(mem) dtProximityGrid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtFreeProximityGrid(dtProximityGrid* ptr)
 | 
			
		||||
{
 | 
			
		||||
	if (!ptr) return;
 | 
			
		||||
	ptr->~dtProximityGrid();
 | 
			
		||||
	dtFree(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
inline int hashPos2(int x, int y, int n)
 | 
			
		||||
{
 | 
			
		||||
	return ((x*73856093) ^ (y*19349663)) & (n-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dtProximityGrid::dtProximityGrid() :
 | 
			
		||||
	m_cellSize(0),
 | 
			
		||||
	m_invCellSize(0),
 | 
			
		||||
	m_pool(0),
 | 
			
		||||
	m_poolHead(0),
 | 
			
		||||
	m_poolSize(0),
 | 
			
		||||
	m_buckets(0),
 | 
			
		||||
	m_bucketsSize(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dtProximityGrid::~dtProximityGrid()
 | 
			
		||||
{
 | 
			
		||||
	dtFree(m_buckets);
 | 
			
		||||
	dtFree(m_pool);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dtProximityGrid::init(const int poolSize, const float cellSize)
 | 
			
		||||
{
 | 
			
		||||
	dtAssert(poolSize > 0);
 | 
			
		||||
	dtAssert(cellSize > 0.0f);
 | 
			
		||||
	
 | 
			
		||||
	m_cellSize = cellSize;
 | 
			
		||||
	m_invCellSize = 1.0f / m_cellSize;
 | 
			
		||||
	
 | 
			
		||||
	// Allocate hashs buckets
 | 
			
		||||
	m_bucketsSize = dtNextPow2(poolSize);
 | 
			
		||||
	m_buckets = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_bucketsSize, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_buckets)
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	// Allocate pool of items.
 | 
			
		||||
	m_poolSize = poolSize;
 | 
			
		||||
	m_poolHead = 0;
 | 
			
		||||
	m_pool = (Item*)dtAlloc(sizeof(Item)*m_poolSize, DT_ALLOC_PERM);
 | 
			
		||||
	if (!m_pool)
 | 
			
		||||
		return false;
 | 
			
		||||
	
 | 
			
		||||
	clear();
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtProximityGrid::clear()
 | 
			
		||||
{
 | 
			
		||||
	memset(m_buckets, 0xff, sizeof(unsigned short)*m_bucketsSize);
 | 
			
		||||
	m_poolHead = 0;
 | 
			
		||||
	m_bounds[0] = 0xffff;
 | 
			
		||||
	m_bounds[1] = 0xffff;
 | 
			
		||||
	m_bounds[2] = -0xffff;
 | 
			
		||||
	m_bounds[3] = -0xffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dtProximityGrid::addItem(const unsigned short id,
 | 
			
		||||
							  const float minx, const float miny,
 | 
			
		||||
							  const float maxx, const float maxy)
 | 
			
		||||
{
 | 
			
		||||
	const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
 | 
			
		||||
	const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
 | 
			
		||||
	const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
 | 
			
		||||
	const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
 | 
			
		||||
	
 | 
			
		||||
	m_bounds[0] = dtMin(m_bounds[0], iminx);
 | 
			
		||||
	m_bounds[1] = dtMin(m_bounds[1], iminy);
 | 
			
		||||
	m_bounds[2] = dtMax(m_bounds[2], imaxx);
 | 
			
		||||
	m_bounds[3] = dtMax(m_bounds[3], imaxy);
 | 
			
		||||
	
 | 
			
		||||
	for (int y = iminy; y <= imaxy; ++y)
 | 
			
		||||
	{
 | 
			
		||||
		for (int x = iminx; x <= imaxx; ++x)
 | 
			
		||||
		{
 | 
			
		||||
			if (m_poolHead < m_poolSize)
 | 
			
		||||
			{
 | 
			
		||||
				const int h = hashPos2(x, y, m_bucketsSize);
 | 
			
		||||
				const unsigned short idx = (unsigned short)m_poolHead;
 | 
			
		||||
				m_poolHead++;
 | 
			
		||||
				Item& item = m_pool[idx];
 | 
			
		||||
				item.x = (short)x;
 | 
			
		||||
				item.y = (short)y;
 | 
			
		||||
				item.id = id;
 | 
			
		||||
				item.next = m_buckets[h];
 | 
			
		||||
				m_buckets[h] = idx;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dtProximityGrid::queryItems(const float minx, const float miny,
 | 
			
		||||
								const float maxx, const float maxy,
 | 
			
		||||
								unsigned short* ids, const int maxIds) const
 | 
			
		||||
{
 | 
			
		||||
	const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
 | 
			
		||||
	const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
 | 
			
		||||
	const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
 | 
			
		||||
	const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
 | 
			
		||||
	
 | 
			
		||||
	int n = 0;
 | 
			
		||||
	
 | 
			
		||||
	for (int y = iminy; y <= imaxy; ++y)
 | 
			
		||||
	{
 | 
			
		||||
		for (int x = iminx; x <= imaxx; ++x)
 | 
			
		||||
		{
 | 
			
		||||
			const int h = hashPos2(x, y, m_bucketsSize);
 | 
			
		||||
			unsigned short idx = m_buckets[h];
 | 
			
		||||
			while (idx != 0xffff)
 | 
			
		||||
			{
 | 
			
		||||
				Item& item = m_pool[idx];
 | 
			
		||||
				if ((int)item.x == x && (int)item.y == y)
 | 
			
		||||
				{
 | 
			
		||||
					// Check if the id exists already.
 | 
			
		||||
					const unsigned short* end = ids + n;
 | 
			
		||||
					unsigned short* i = ids;
 | 
			
		||||
					while (i != end && *i != item.id)
 | 
			
		||||
						++i;
 | 
			
		||||
					// Item not found, add it.
 | 
			
		||||
					if (i == end)
 | 
			
		||||
					{
 | 
			
		||||
						if (n >= maxIds)
 | 
			
		||||
							return n;
 | 
			
		||||
						ids[n++] = item.id;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				idx = item.next;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dtProximityGrid::getItemCountAt(const int x, const int y) const
 | 
			
		||||
{
 | 
			
		||||
	int n = 0;
 | 
			
		||||
	
 | 
			
		||||
	const int h = hashPos2(x, y, m_bucketsSize);
 | 
			
		||||
	unsigned short idx = m_buckets[h];
 | 
			
		||||
	while (idx != 0xffff)
 | 
			
		||||
	{
 | 
			
		||||
		Item& item = m_pool[idx];
 | 
			
		||||
		if ((int)item.x == x && (int)item.y == y)
 | 
			
		||||
			n++;
 | 
			
		||||
		idx = item.next;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user