simvx.core.navigation3d

3D navigation mesh system — navmesh generation, A* pathfinding, agents, obstacles.

Provides NavigationMesh3D for defining walkable surfaces, NavigationServer3D as a singleton coordinator, NavigationAgent3D for autonomous path-following, and NavigationObstacle3D for dynamic obstacle avoidance.

Module Contents

Classes

NavigationMesh3D

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

NavigationServer3D

Global navigation server that manages all registered navigation regions.

NavigationRegion3D

Node that holds a NavigationMesh3D and registers it with the server.

NavigationAgent3D

3D pathfinding agent that follows paths on the navigation mesh.

NavigationObstacle3D

Dynamic obstacle that affects navigation agent avoidance.

Data

API

simvx.core.navigation3d.log[source]

‘getLogger(…)’

simvx.core.navigation3d.__all__

[‘NavigationMesh3D’, ‘NavigationRegion3D’, ‘NavigationAgent3D’, ‘NavigationServer3D’, ‘NavigationObs…

class simvx.core.navigation3d.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

(N, 3) float32 array of mesh vertices.

property triangles: numpy.ndarray

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

property triangle_count: int
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. Any navmesh triangle whose centroid falls inside the obstacle polygon (or whose area overlaps it significantly) is marked as blocked and excluded from pathfinding.

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) list[simvx.core.math.types.Vec3][source]

A* pathfinding on the navmesh triangle graph.

Finds the shortest path from start to end by searching through connected triangles. Returns a list of waypoints (triangle centroids + start/end) forming the path, or an empty list if no path exists.

Args: start: Start position in world space. end: End position in world space.

Returns: List of Vec3 waypoints, or empty list if unreachable.

get_closest_point(point: simvx.core.math.types.Vec3) simvx.core.math.types.Vec3[source]

Snap a point to the nearest navmesh surface.

Args: point: Query point in world space.

Returns: Closest point on the navmesh surface.

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 the navmesh.

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 navmesh.

Args: center: Center of the sampling sphere. radius: Maximum distance from center. max_attempts: Number of random samples to try.

Returns: A random point on the navmesh within radius, or None if not found.

class simvx.core.navigation3d.NavigationServer3D[source]

Global navigation server that manages all registered navigation regions.

Provides unified pathfinding and spatial queries across all active regions.

Initialization

classmethod get_singleton() simvx.core.navigation3d.NavigationServer3D[source]

Return the global NavigationServer3D instance (created on first access).

classmethod reset() None[source]

Reset the singleton (useful for testing).

register_region(region: simvx.core.navigation3d.NavigationRegion3D) None[source]

Register a navigation region.

unregister_region(region: simvx.core.navigation3d.NavigationRegion3D) None[source]

Unregister a navigation region.

register_obstacle(obstacle: simvx.core.navigation3d.NavigationObstacle3D) None[source]

Register a dynamic obstacle.

unregister_obstacle(obstacle: simvx.core.navigation3d.NavigationObstacle3D) None[source]

Unregister a dynamic obstacle.

find_path(start: simvx.core.math.types.Vec3, end: simvx.core.math.types.Vec3) list[simvx.core.math.types.Vec3][source]

Find a path from start to end across all active navigation regions.

Queries each enabled region and returns the shortest valid path found.

Args: start: Start position in world space. end: End position in world space.

Returns: List of Vec3 waypoints, or empty list if unreachable.

get_closest_point(point: simvx.core.math.types.Vec3) simvx.core.math.types.Vec3[source]

Find the closest point on any active navmesh surface.

Args: point: Query point in world space.

Returns: Closest point on any navmesh, or the input point if no regions exist.

property obstacles: list[simvx.core.navigation3d.NavigationObstacle3D]

All registered dynamic obstacles.

class simvx.core.navigation3d.NavigationRegion3D(navigation_mesh: simvx.core.navigation3d.NavigationMesh3D | None = None, **kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

Node that holds a NavigationMesh3D and registers it with the server.

Attach a NavigationMesh3D to this node and add it to the scene tree to make its walkable surface available for pathfinding.

Initialization

enabled

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.navigation3d.NavigationAgent3D(**kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

3D pathfinding agent that follows paths on the navigation mesh.

Computes a path to target_position and advances along it each physics frame. Emits navigation_finished when the target is reached.

Attach as a child of a Node3D whose position you want to steer.

Initialization

target_desired_distance

‘Property(…)’

path_desired_distance

‘Property(…)’

max_speed

‘Property(…)’

avoidance_radius

‘Property(…)’

property target_position: simvx.core.math.types.Vec3
is_navigation_finished() bool[source]

Check if the agent has reached its target or has no path.

get_next_path_position() simvx.core.math.types.Vec3[source]

Get the next waypoint position the agent is heading toward.

Returns: Next waypoint, or current position if path is empty.

physics_process(dt: float) None[source]

Advance along the path, computing steering velocity.

render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.navigation3d.NavigationObstacle3D(position=None, rotation=None, scale=None, **kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

Dynamic obstacle that affects navigation agent avoidance.

Place in the scene tree to create areas that agents will steer around. Does not carve the navmesh — instead, agents detect obstacles at runtime and adjust their velocity to avoid collisions.

Initialization

radius

‘Property(…)’

height

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()