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

‘getLogger(…)’

simvx.core.navigation3d.__all__

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

class simvx.core.navigation3d.NavigationMesh3D

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]) None

Add a walkable polygon (auto-triangulated via fan from first vertex).

Args: vertices: 3+ coplanar points defining the polygon boundary.

add_triangle(v0: simvx.core.math.types.Vec3, v1: simvx.core.math.types.Vec3, v2: simvx.core.math.types.Vec3) None

Add a single walkable triangle.

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

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]

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

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

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

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

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

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

classmethod reset() None

Reset the singleton (useful for testing).

register_region(region: simvx.core.navigation3d.NavigationRegion3D) None

Register a navigation region.

unregister_region(region: simvx.core.navigation3d.NavigationRegion3D) None

Unregister a navigation region.

register_obstacle(obstacle: simvx.core.navigation3d.NavigationObstacle3D) None

Register a dynamic obstacle.

unregister_obstacle(obstacle: simvx.core.navigation3d.NavigationObstacle3D) None

Unregister a dynamic obstacle.

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

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

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)

Bases: simvx.core.engine.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
exit_tree() None
property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property global_position: simvx.core.math.types.Vec3
property global_rotation: simvx.core.math.types.Quat
property global_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)
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
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) 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)
destroy()
queue_free

None

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]
get_settings

None

__repr__()
class simvx.core.navigation3d.NavigationAgent3D(**kwargs)

Bases: simvx.core.engine.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

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

get_next_path_position() simvx.core.math.types.Vec3

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

Advance along the path, computing steering velocity.

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property global_position: simvx.core.math.types.Vec3
property global_rotation: simvx.core.math.types.Quat
property global_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)
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
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) 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)
destroy()
queue_free

None

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]
get_settings

None

__repr__()
class simvx.core.navigation3d.NavigationObstacle3D(position=None, rotation=None, scale=None, **kwargs)

Bases: simvx.core.engine.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
exit_tree() None
property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property global_position: simvx.core.math.types.Vec3
property global_rotation: simvx.core.math.types.Quat
property global_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)
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
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) 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)
destroy()
queue_free

None

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]
get_settings

None

__repr__()