simvx.core.nodes_3d.camera¶
Camera3D, OrbitCamera3D – 3D camera nodes.
Module Contents¶
Classes¶
3D camera providing view and projection matrices. |
|
Camera that orbits a pivot point, for editors, inspectors and strategy views. |
Data¶
API¶
- simvx.core.nodes_3d.camera.ORBIT_DRAG¶
‘orbit_camera.drag’
- simvx.core.nodes_3d.camera.ORBIT_STICK¶
‘orbit_camera.orbit’
- simvx.core.nodes_3d.camera.ORBIT_ZOOM¶
‘orbit_camera.zoom’
- simvx.core.nodes_3d.camera.ORBIT_PAN¶
‘orbit_camera.pan’
- simvx.core.nodes_3d.camera.ORBIT_CAMERA_ACTIONS¶
()
- class simvx.core.nodes_3d.camera.Camera3D(look_at=None, up=None, **kwargs)[source]¶
Bases:
simvx.core.nodes_3d.node3d.Node3D3D camera providing view and projection matrices.
The first
Camera3Dfound in the scene tree is used by the renderer. Position and orientation are inherited fromNode3D; the camera adds projection parameters (field of view, clip planes).Conventions (full reference:
docs/graphics/cameras.md):Right-handed, Y-up.
Forward = local -Z.
camera.forwardisworld_rotation * (0, 0, -1). Matches Godot / glTF / OpenGL; clashes with Unity / Unreal (+Z forward): assets ported from those engines must negate the forward vector once at import.look_at(target)and :meth:~Node3D.face_alongboth align the local -Z axis with the supplied direction.Internal angles in radians;
fovis stored in degrees only as an inspector convenience.Matrices are row-major; the renderer transposes at the GPU boundary and applies a Vulkan-only Y-flip in
- meth:
projection_matrix.
Attributes: projection:
"perspective"(default) or"orthographic". Selects which of the two parameter sets below builds :meth:projection_matrix; the other set is ignored but retained, so toggling back and forth is lossless. fov: Vertical field of view in degrees (1 – 179). Perspective only. ortho_size: Full vertical height of the orthographic view volume in world units, matching Godot’sCamera3D.size: at20.0the camera sees 10 units above and 10 below its centre line, and the horizontal extent follows from the aspect ratio. Orthographic only. near: Near clip plane distance. far: Far clip plane distance. clip_plane: Optional world-space oblique near-clip plane(nx, ny, nz, d)with equationn . x + d = 0; geometry on then . x + d > 0side is kept.None(default) keeps the standard near plane. Set per frame by :func:~simvx.core.planar_reflection_cameraso a mirrored camera never renders geometry behind the reflection plane (Lengyel oblique-frustum technique). A plain runtime attribute, not a serialised Property: it is a derived, per-frame value.Example::
camera = Camera3D(position=(0, 5, 10), look_at=(0, 0, 0), fov=75.0) isometric = Camera3D( position=(10, 10, 10), look_at=(0, 0, 0), projection="orthographic", ortho_size=16.0, )Initialization
- projection¶
‘Property(…)’
- fov¶
‘Property(…)’
- ortho_size¶
‘Property(…)’
- near¶
‘Property(…)’
- far¶
‘Property(…)’
- exposure¶
‘Property(…)’
- cull_mask¶
‘Bitmask(…)’
- gizmo_colour¶
‘Colour(…)’
- set_cull_mask_layer(index: int, enabled: bool = True) None[source]¶
Enable or disable a specific cull mask layer (0-31).
- is_cull_mask_layer_enabled(index: int) bool[source]¶
Check if a specific cull mask layer is enabled (0-31).
- get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]¶
Return view-volume wireframe lines: a frustum, or a box when orthographic.
- property view_matrix: numpy.ndarray[source]¶
View matrix computed from this node’s global transform.
Cached: the matrix is rebuilt only after the camera’s transform changes (its own or an ancestor’s), so projecting many points per frame pays for one build. Each read returns a fresh copy that the caller may mutate freely.
Returns: 4x4 view matrix as numpy array (row-major)
- projection_matrix(aspect: float = 16 / 9) numpy.ndarray[source]¶
Projection matrix for the given aspect ratio (Vulkan clip space).
Built from :attr:
fovwhen :attr:projectionis"perspective"and from :attr:ortho_sizewhen it is"orthographic". In both cases the vertical extent is the one held fixed and the horizontal extent follows from aspect, so widening the window reveals more of the scene rather than stretching it.Cached: the matrix is rebuilt only when :attr:
projection,- Attr:
fov, :attr:ortho_size, :attr:near, :attr:faror aspect differ from the previous call. Each read returns a fresh array that the caller may mutate freely. An oblique :attr:clip_planeis applied on top of the cached base per call, since it and the view matrix it depends on change per frame.
Args: aspect: Aspect ratio (width / height)
Returns: 4x4 projection matrix as numpy array (row-major) Includes Y-flip for Vulkan rendering
- ray_at(screen_pos) simvx.core.math.raycast.Ray[source]¶
The world-space ray through screen_pos, a point in this camera’s view.
screen_pos is in the units of :attr:
viewport_size,(0, 0)at the top left, soInput.mouse_positionpasses straight in. The ray starts on the near plane; under an orthographic projection every ray is parallel to :attr:forward.
- world_to_screen(point) simvx.core.math.types.Vec2 | None[source]¶
Where the world-space point lands in this camera’s view, or
None.The inverse of :meth:
ray_at, in the same units. A point outside the view volume (off to one side, or past the far plane) still returns its position, off screen, which is what an edge indicator wants. A point level with or behind the camera has no position on screen and returnsNone.
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- render_layer¶
‘Property(…)’
- 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)¶
- face_along(forward: tuple[float, float, float] | numpy.ndarray, up: tuple[float, float, float] | numpy.ndarray | None = None) 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
- dev_checks: ClassVar[bool]¶
None
- dev_warnings: ClassVar[bool]¶
None
- freeze_check_interval: ClassVar[float]¶
1.0
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- focus_mode¶
‘Property(…)’
- drag_threshold¶
‘Property(…)’
- focus_entered¶
‘Signal(…)’
- focus_exited¶
‘Signal(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- property input_player: simvx.core.input.players.InputPlayer¶
- property input_context: simvx.core.input.contexts.InputContext | None¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(canvas) None¶
- on_draw_3d(gizmos) None¶
- on_picked(event: simvx.core.input.events.PickEvent) bool | None¶
- on_gui_input(event: simvx.core.input.events.GuiInputEvent) bool | None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- property focused: bool¶
- property focus_rect: tuple[float, float, float, float] | None¶
- drag_threshold_for(pointer_type: simvx.core.input.enums.PointerType | str) float¶
- grab_focus() None¶
- release_focus() None¶
- capture_pointer(pointer_id: int | None = None) None¶
- release_pointer(pointer_id: int | None = None) None¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- property destroyed: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property viewport_size: simvx.core.math.types.Vec2¶
- property viewport_insets: tuple[float, float, float, float]¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.nodes_3d.camera.OrbitCamera3D(**kwargs)[source]¶
Bases:
simvx.core.nodes_3d.camera.Camera3DCamera that orbits a pivot point, for editors, inspectors and strategy views.
The camera’s
positionand orientation are derived: assigning- Attr:
pivot, :attr:distance, :attr:yawor :attr:pitch, from code, a scene file or the inspector, recomputes them at once, and the camera always looks at the pivot. Assigningpositiondirectly is therefore pointless, since the next orbit change overwrites it: move :attr:pivotinstead. Assign the pivot whole (cam.pivot = targetorcam.pivot += offset); writing one component in place (cam.pivot.x = 3) is not seen.
It takes its own input. Adding the node is the whole of it: dragging with the primary pointer (a left click, a pen tip, one finger) or pushing the controller’s right stick orbits, a two-finger pinch, the mouse wheel and the triggers zoom (one action, in the pinch’s unit, each source scaled into it by its binding), and with :attr:
pan_enableda secondary or middle drag pans. The bindings are the camera’s owninput_context, under the names in- Data:
ORBIT_CAMERA_ACTIONS, namespaced so they never take a game’s names. A player changes one withInputMap.rebind("orbit_camera.drag", Mouse.RIGHT); a game replaces the set by assigninginput_context, and turns the camera’s input off with :attr:interactive.
A press a UI control claimed never reaches the camera, so dragging a slider does not also orbit. The camera hears input exactly while it can receive it: a paused or disabled camera stands still, and a context that hides everything below it (a pause menu) takes the camera’s names too. Two orbit cameras in one tree both answer; set :attr:
interactiveon the one that is not being looked through.- Meth:
orbit, :meth:panand :meth:zoomare the controls the input drives, and a game calls them too (an auto-orbit, a scripted fly-in).
Angles are radians, like everywhere else in the engine. Yaw sweeps around the world Y axis; negative pitch places the camera above the pivot looking down, which is why the default view is a raised three-quarter angle. Every assignment clamps pitch just short of the poles, where the view direction would be undefined, and distance to at least
0.01.- Meth:
orbitfurther clamps pitch to :attr:min_pitchand- Attr:
max_pitch, and :meth:zoomdistance to :attr:min_distanceand- Attr:
max_distance; those are the player’s limits, and direct assignment is not held to them.
Example::
cam = OrbitCamera3D(pivot=Vec3(0, 1, 0), distance=12.0, max_distance=40.0) cam.orbit(math.radians(15.0), 0.0) # sweep 15 degrees to the side cam.zoom(2.0) # move 2 units closer
Initialization
- input_context: simvx.core.input.contexts.InputContext | None¶
‘InputContext(…)’
- pivot¶
‘Property(…)’
- distance¶
‘Property(…)’
- yaw¶
‘Property(…)’
- pitch¶
‘Property(…)’
- min_distance¶
‘Property(…)’
- max_distance¶
‘Property(…)’
- min_pitch¶
‘Property(…)’
- max_pitch¶
‘Property(…)’
- interactive¶
‘Property(…)’
- drag_sensitivity¶
‘Property(…)’
- stick_speed¶
‘Property(…)’
- pan_enabled¶
‘Property(…)’
- pan_speed¶
‘Property(…)’
- invert_x¶
‘Property(…)’
- invert_y¶
‘Property(…)’
- orbit(dyaw: float, dpitch: float)[source]¶
Orbit the camera around its pivot point by yaw and pitch deltas (radians).
- zoom(delta: float)[source]¶
Zoom the camera by adjusting its distance to the pivot: positive moves closer.
- projection¶
‘Property(…)’
- fov¶
‘Property(…)’
- ortho_size¶
‘Property(…)’
- near¶
‘Property(…)’
- far¶
‘Property(…)’
- exposure¶
‘Property(…)’
- cull_mask¶
‘Bitmask(…)’
- gizmo_colour¶
‘Colour(…)’
- on_enter_tree()¶
- set_cull_mask_layer(index: int, enabled: bool = True) None¶
- is_cull_mask_layer_enabled(index: int) bool¶
- get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]]¶
- property view_matrix: numpy.ndarray¶
- projection_matrix(aspect: float = 16 / 9) numpy.ndarray¶
- ray_at(screen_pos) simvx.core.math.raycast.Ray¶
- world_to_screen(point) simvx.core.math.types.Vec2 | None¶
- position¶
‘_SpatialVecProperty(…)’
- rotation¶
‘Property(…)’
- scale¶
‘_SpatialVecProperty(…)’
- render_layer¶
‘Property(…)’
- 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)¶
- face_along(forward: tuple[float, float, float] | numpy.ndarray, up: tuple[float, float, float] | numpy.ndarray | None = None) 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
- dev_checks: ClassVar[bool]¶
None
- dev_warnings: ClassVar[bool]¶
None
- freeze_check_interval: ClassVar[float]¶
1.0
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- focus_mode¶
‘Property(…)’
- drag_threshold¶
‘Property(…)’
- focus_entered¶
‘Signal(…)’
- focus_exited¶
‘Signal(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- property input_player: simvx.core.input.players.InputPlayer¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(canvas) None¶
- on_draw_3d(gizmos) None¶
- on_picked(event: simvx.core.input.events.PickEvent) bool | None¶
- on_gui_input(event: simvx.core.input.events.GuiInputEvent) bool | None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- property focused: bool¶
- property focus_rect: tuple[float, float, float, float] | None¶
- drag_threshold_for(pointer_type: simvx.core.input.enums.PointerType | str) float¶
- grab_focus() None¶
- release_focus() None¶
- capture_pointer(pointer_id: int | None = None) None¶
- release_pointer(pointer_id: int | None = None) None¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- property destroyed: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property viewport_size: simvx.core.math.types.Vec2¶
- property viewport_insets: tuple[float, float, float, float]¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶