simvx.core.navigation3d.mesh

NavigationMesh3D: triangle-based navmesh with A* pathfinding.

Module Contents

Classes

NavigationMesh3D

3D navigation mesh with triangle-based A* pathfinding.

Data

log

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

property vertices: numpy.ndarray[source]

(N, 3) float32 array of mesh vertices.

property triangles: numpy.ndarray[source]

(M, 3) int32 array of triangle vertex indices.

property triangle_count: int[source]
add_polygon(vertices: list[simvx.core.math.types.Vec3], cell_size: float = 0.0) None[source]

Add a walkable polygon.

Without cell_size the polygon is fan-triangulated from the first vertex (fast, 2 triangles for a quad). When cell_size is 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_size to :meth:add_polygon to make those triangles small enough that the difference does not matter.

Args: vertices: 3+ points defining the obstacle boundary (Y is ignored).

clear() None[source]

Remove all polygons, obstacles, and cached data.

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_distance is 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_obstacle are 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 point and 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.