simvx.core.navigation3d.mesh¶
NavigationMesh3D: triangle-based navmesh with A* pathfinding.
Module Contents¶
Classes¶
3D navigation mesh with triangle-based A* pathfinding. |
Data¶
API¶
- simvx.core.navigation3d.mesh.log¶
‘getLogger(…)’
- class simvx.core.navigation3d.mesh.NavigationMesh3D[source]¶
3D navigation mesh with triangle-based A* pathfinding.
Stores walkable geometry as triangles and builds an adjacency graph for pathfinding. Supports manual polygon insertion and automated bake from level geometry.
Initialization
- add_polygon(vertices: list[simvx.core.math.types.Vec3], cell_size: float = 0.0) → None[source]¶
Add a walkable polygon.
Without
cell_sizethe polygon is fan-triangulated from the first vertex (fast, 2 triangles for a quad). Whencell_sizeis positive the polygon’s axis-aligned bounding box is subdivided into a regular grid of right-triangles, keeping only cells whose centres fall inside the polygon. The finer mesh is required for obstacle carving to work at useful resolution.Args: vertices: 3+ coplanar points defining the polygon boundary. cell_size: When > 0, grid cell size for subdivision. Typical values: 0.5 – 2.0 depending on obstacle density.
- add_triangle(v0: simvx.core.math.types.Vec3, v1: simvx.core.math.types.Vec3, v2: simvx.core.math.types.Vec3) → None[source]¶
Add a single walkable triangle.
- add_obstacle(vertices: list[simvx.core.math.types.Vec3]) → None[source]¶
Subtract an obstacle region from the walkable area.
The polygon is projected onto the XZ plane. Every navmesh triangle whose area overlaps it, by any amount, is marked as blocked and excluded from pathfinding and from the spatial queries.
Carving works at the resolution of the mesh, since a triangle is either blocked or it is not. The hole is therefore never smaller than the obstacle, and never larger than the obstacle grown by one triangle of the mesh it is cut from. Pass
cell_sizeto :meth:add_polygonto make those triangles small enough that the difference does not matter.Args: vertices: 3+ points defining the obstacle boundary (Y is ignored).
- bake_from_geometry(mesh_vertices: numpy.ndarray, mesh_indices: numpy.ndarray, agent_radius: float = 0.5, agent_height: float = 2.0, max_slope: float = 45.0, cell_size: float = 0.3, cell_height: float = 0.2) → None[source]¶
Generate navmesh from level geometry using simplified Recast-style algorithm.
Steps: 1. Voxelize geometry into a height field 2. Mark walkable voxels (slope < max_slope, clearance > agent_height) 3. Build regions from connected walkable areas 4. Extract contours and triangulate
Args: mesh_vertices: (N, 3) float32 array of source mesh vertices. mesh_indices: (M, 3) int32 array of triangle indices. agent_radius: Agent capsule radius for erosion. agent_height: Minimum clearance height. max_slope: Maximum walkable slope in degrees. cell_size: Horizontal voxel size. cell_height: Vertical voxel size.
- find_path(start: simvx.core.math.types.Vec3, end: simvx.core.math.types.Vec3, max_distance: float = float('inf')) → list[simvx.core.math.types.Vec3][source]¶
A* pathfinding on the navmesh triangle graph.
Finds the shortest route from start to end by searching through connected, unblocked triangles. The returned waypoints are triangle centroids bracketed by the start and end pulled sideways onto the walkable surface, so a route that skirts a carved obstacle does not finish inside it. That is a move in the XZ plane only: an endpoint keeps the height it was given, and an endpoint already over walkable ground within
max_distanceis returned untouched.Where storeys stack over one footprint, an endpoint is matched to the one whose surface is vertically nearest it, so an agent on a gallery is routed along the gallery rather than along the floor beneath it.
Args: start: Start position in world space. end: End position in world space. max_distance: How far start or end may lie from the walkable surface, measured in three dimensions, and still be accepted. A point off the mesh, inside a carved obstacle, or hovering over the mesh footprint further than this above or below it, is moved to the nearest walkable triangle when one is within this distance, and the query fails otherwise. Unbounded by default.
Returns: List of Vec3 waypoints, or empty list if unreachable.
- get_closest_point(point: simvx.core.math.types.Vec3, max_distance: float) → simvx.core.math.types.Vec3 | None[source]¶
Snap a point to the nearest walkable navmesh surface.
Triangles carved away by :meth:
add_obstacleare not walkable and are never returned, so the result is always somewhere an agent may stand.Args: point: Query point in world space. max_distance: How far the surface may be from
pointand still count as a result.Returns: The closest point on a walkable triangle, or None when the mesh is empty, fully carved away, or has no walkable surface that near.
- is_point_on_mesh(point: simvx.core.math.types.Vec3, tolerance: float = 0.5) → bool[source]¶
Test if a point is on the walkable area.
Args: point: Query point in world space. tolerance: Maximum distance from navmesh surface to still count.
Returns: True if point is within tolerance of a walkable triangle. A point inside a carved obstacle is not on the mesh.
- sample_position(center: simvx.core.math.types.Vec3, radius: float, max_attempts: int = 30) → simvx.core.math.types.Vec3 | None[source]¶
Sample a random point near center that lies on the walkable navmesh.
Args: center: Center of the sampling sphere. radius: Maximum distance from center. max_attempts: Number of random samples to try.
Returns: A random walkable point within radius of center, or None if no attempt landed on one. Carved obstacles are never sampled.