Package: pynavmesh
Python Path Finder moduleIt placed on thepythonfolder of the repo. This folder contains:main modulepathfindersome examplessmall PySide6 application for exploring navigation meshesHow to useTo install:pip install pynavmeshTo baking navigation mesh:from pathfinder import navmesh_baker as nmb
# create baker object
baker = nmb.NavmeshBaker()
# add geometry, for example a simple plane
# the first array contains vertex positions, the second array contains polygons of the geometry
baker.add_geometry([(-4.0, 0.0, -4.0), (-4.0, 0.0, 4.0), (4.0, 0.0, 4.0), (4.0, 0.0, -4.0)], [[0, 1, 2, 3]])
# bake navigation mesh
baker.bake()
# obtain polygonal description of the mesh
vertices, polygons = baker.get_polygonization()baker.bake()can accept several parameters. The most important of them are:cell_sizeis a voxel size, used for rasterization input polygonscell_heightis a vozel heightagent_heightsuppositional agent sizeagent_radiussuppositional agent radiusThe following parameters define walkable areas:agent_max_climb,agent_max_slope.Other parameters define polygonization settings:region_min_sizeislands with less area will be ignoredregion_merge_sizeedge_max_lenmaximum length of polygon edgesedge_max_errorverts_per_polydefine maximum number of corners in polygons (if this value is equal, for eaxmple, to 3, then all polygons will be triangles)detail_sample_distancedetail_sample_maximum_errorTo store generated navigation mesh to the file you can use one of two functions:baker.save_to_text(file_path)to save polygonal description of the navigation mesh into simple text filebaker.save_to_binary(file_path)to save polygonal description of the navigation mesh into binary file with the same structureTo load navigation mesh data from file you can use similar functions frompathfindermodule:read_from_text(file_path)read_from_binary(file_path)Both functions return 2-tuple(vertices, polygons), whereverticesis array of 3-tuples with vertex coordinates,polygonsis array of integer arrays with polygon vertex indices.To create path finder object:import pathfinder as pf
pathfinder = pf.PathFinder(vertices, polygons)verticesis an array of the form[(x1, y1, z1), (x2, y2, z2), ...]and contains vertex coordinatespolygonsis an array of the form[[p11, p12, ..., p1n1], [p21, p22, p23, ..., p2n2], ...], where each array is a sequence of vertices, which form the polygon. Eachpijis an integer - the index of the vertex in theverticesarrayTo find the path in the navigation mesh simply callpath = pathfinder.search_path(start, finish)startandfinishare tuples with coordinates. It return the arraypath, which contains the sequence of corners of it linear segments.For using agents collision avoidance it needs more detail setup. When creatingpathfinderobject you can setup additional parameters:neighbor_dist- search distance for other agentsmax_neighbors- how many agents considered fir collision avoidancetime_horizonandtime_horizon_obst- how close (in relative time) the agent should be to the other agent or collider to avoid itmax_speed- maximum agent speedagent_radius- the default agent radius. This value used for building collisions (in the navigation mesh boundary) for agentsupdate_path_find- how often (the interval in seconds) the system recalculate path for agentscontinuous_moving- should agents move after they achieve the target point or notmove_agents- ifTruethen the system move agent by internal algorithm, in other case it only calculate velocitiessnap_to_navmesh- ifTruethen snap agents to the navigation mesh during the simulationHow the agents collision avoidance works:Ifverticesorpolygonsarrays areNonethen the system skip building navigation mesh and in this case it allows to simulate agents in infinite 2d-planeImplemented RVO2 algorithm assume that agent move in the plane XZ. Navigation mesh can be on different height level, but all velocities calculations use only the first (X) and third (Z) coordinates.It's possible to use original RVO2 compiled python module. The repository containsbinary_rvofolder. Copy *.pyd file from this folder with specific python version to\pathfinder\pyrvo\folder and in__init__.pyfile setIMPORT_BINARY = True. Binary version is faster but does not allows to delete agents from simulations.To add agent into simulationpathfinder.add_agent(position, radius, speed)wherepositionis 3-tuple of floats. This method returns the id of the new agent, his name. All agents have different unique ids.To delete the agent from simulationpathfinder.delete_agent(agent_id)To set the agent destination pointpathfinder.set_agent_destination(agent_id, position)Examplesbaker_examples.pycontains some basic examples, where we load some geometry into baker and generate corresponding navigation mesh.bvh_examples.pycontains small benchmark for testing the speed of using bvh in the navigation mesh object. It create grid of square polygons, sample random positions and find the index of the closest polygon to each position.graph_examples.pycontains examples for creating graphs and using A* algorithm for finding the shortest path in it. One of them creates grid-like graph with randomly erased edges and find path between random vertices. The result is plotted by usingmatplotlib.navmesh_examples.pycontains some examples, which demonstrates, how to create navigation mesh from raw data and how to find the path between points. There is an example, where navigation mesh created by using raw data from the text file.rvo_examples.pycontains examples how to use raw RVO simulation and also as a sub-object of thePathFinder.Exploring applicationThis application based onNavmesh Explorer. Allows to load navigation mesh data from text file, add and remove agents from this navigation mesh and move it from start to end points. Two fileslevel_triangles.txtandlevel_polygons.txtare examples of text files, which supported by application. If the navigation mesh is triangulated, then the file should contains only two rows. The first one contains plain array of vertex coordinates (divided by spaces), the second row contains plain array of triangle vertex indexes. If the navigation mesh defined by polygonal description, then the file should contains three rows. The first one contains vertex coordinates, the second one - the sequence of polygon corners, and the third one - sizes of polygons.In some cases the path is not optimal. It depends on polygon decomposition. Here is an example.Start and finish points are in top and bottom polygons. At first step the algorithm finds the shortest path between these two polygons. But the mesh is symmetric, and that's why there are two equal paths - at the left side and at the right side. The algorithm select one of them (at the right side), and form result path by connecting it with input points. So, it produce non-optimal result.NavmeshBaker APIbaker = NavmeshBaker()Create a baker object.baker.add_geometry(vertices: List[Tuple[float, float, float], polygons: List[List[int]])Add input polygonal data to the baker.verticesis an array of 3-tuples with vertex positions,polygonsis array of integer arrays with polygon indexes. If you need add several meshes to the baker, you can call this method several times with different arguments. Polygon indices should be the same as tuples indices in inverticesarray.baker.bake()Generate navigation mesh polygonal description.baker.get_polygonization()Return polygonal description of the baked navigation mesh as 2-tuple(vertices, polygons).baker.save_to_binary(file_path: str)Save navigation mesh polygonal description into binary file.baker.save_to_text(file_path: str)Save navigation mesh polygonal description into text file.PathFinder APIpathfinder = PathFinder(vertices: Optional[List[Tuple[float, float, float]]] = None, 
						polygons: Optional[List[List[int]]] = None,
						neighbor_dist: float = 1.5,
						max_neighbors: int = 5,
						time_horizon: float = 1.5,
						time_horizon_obst: float = 2.0,
						max_speed: float = 10.0,
						agent_radius: float = 0.2,
						update_path_find: float = 1.0,
						continuous_moving: bool = False,
						move_agents: bool = True,
						snap_to_navmesh: bool = False)Create a new pathfinder object.verticesandpolygonsused for navigation mesh and obstacles in RVO. Other parameters used for RVO. Ifcontinuous_movingisTruethen all agents always try to go to the destination points. Even the are already achieve it. Ifmove_agentsisFalsethen eachupdate()method call does not change agents positions, but only recalculate an optimal velocities. Ifsnap_to_navmeshisTruethen after each simulation step it check is agents placed on the navigation mesh. If someone is pushed from the navigation mesh, then it change it position to the closest point on the mesh.pathfinder.add_agent(position: Tuple[float, float, float], radius: float, speed: float)Add new agent to the RVO simulation. Return an id of the new agent.pathfinder.delete_agent(agent_id: int)Remove agent from the simulation. Actual remove process start beforeupdate()method call.pathfinder.set_agent_destination(agent_id, position: Tuple[float, float, float])Set the target position for the agent. The pathfinder object find the shortest path from the current agent position to the destination point and start move the agent along this path.pathfinder.get_all_agents_positions()Return an array with positions of all agents in the simulation. Positions are 2d and contains only X and Z coordinates.pathfinder.get_all_agents_paths()Return an array with paths of all agents in the simulation.pathfinder.get_all_agents_activities()Return an array withTrue/Falsevalues for all agents in the simulation.Falsevalue means that corresponding agent does not move, because it comes to the final target of the path, or path is not defined.pathfinder.get_agents_id()Return agent ids. Use the same order as inget_all_agents_positions(),get_all_agents_paths()andget_all_agents_activities().pathfinder.get_agents_count()Return total agent count in RVO simulation.pathfinder.get_active_agents_count()Return the number of active agents.pathfinder.get_default_agent_radius()Return default agent radius.pathfinder.get_agent_position(agent_id: int)Return position of an agent with a given id.pathfinder.get_agent_path(agent_id: int)Return move path for the given agent.pathfinder.update_time()Simply increase internal timer.pathfinder.update()Update RVO simulation. Ifmove_agents = Truethen also change agent positions. The actual move shift values depends on agent speeds, calculated velocities and time between current call and previousupdate()orupdate_time()methods.pathfinder.search_path(start: Tuple[float, float, float], finish: Tuple[float, float, float], length_limit_coefficient: Optional[float] = None)Return shortest path between start and finish point in the navigation mesh. If navigation mesh is not defined, then return the straight segment between start and finish positions. Parameterlength_limit_coefficientshould be used to more accurate result of the shortest path. In some cases the shortest path in the graph does not lead to the shortest path in the navmesh (because different sizes of polygons). In this case it's possible to definelength_limit_coefficientparameter. In this case the algorithm will search all paths between input points with length in the interval from minimal length to multiplied length. This parameter should be used very carefully because in large navmeshes in can leads to the combinatorial explosion.pathfinder.sample(point: Tuple[float, float, float], is_slow: bool = False)Return the point on the navigation mesh, closest to the input point. ifis_slow = Truethen check each triangles in the mesh and always return the answer. Ifis_slow = Falsethen use more optimal approach and search closed triangles of the mesh in BVH. ReturnNoneif navmesh is not initialized or it fails to find the closest point.