simvx.core.nodes_3d.lights¶
Light3D, DirectionalLight3D, PointLight3D, SpotLight3D – 3D light nodes.
Module Contents¶
Classes¶
Base class for the 3D light nodes. |
|
Infinitely distant light: a sun. Every fragment is lit from one direction. |
|
Omnidirectional light at the node’s world position, with a hard range. |
|
Cone-shaped light aimed along the node’s forward vector. |
API¶
- class simvx.core.nodes_3d.lights.Light3D(**kwargs)[source]¶
Bases:
simvx.core.nodes_3d.node3d.Node3DBase class for the 3D light nodes.
Holds the properties every light shares:
colour,intensity,shadowsandlight_cull_mask. Add one of the concrete subclasses (:class:DirectionalLight3D, :class:PointLight3D, :class:SpotLight3D) to a scene rather than this class.The renderer multiplies
colourbyintensityto get the light’s radiance and feeds that into the Cook-Torrance BRDF, sointensityis a plain linear multiplier: it is not lumens, watts or any photometric unit, and 2.0 is exactly twice as bright as 1.0. Values stay linear all the way to the tonemapper, so overdriving a light past 1.0 is normal and produces bloom rather than clipping.A light only lights a mesh when
light_cull_maskand the mesh’s render layer share a bit, and only when the active camera’s cull mask also overlapslight_cull_mask: use this to keep, say, a UI-only rim light off the world geometry.Initialization
- colour¶
‘Colour(…)’
- intensity¶
‘Property(…)’
- shadows¶
‘Property(…)’
- light_cull_mask¶
‘Bitmask(…)’
- set_light_cull_mask_layer(index: int, enabled: bool = True) None[source]¶
Enable or disable a specific light cull mask layer (0-31).
- is_light_cull_mask_layer_enabled(index: int) bool[source]¶
Check if a specific light cull mask layer is enabled (0-31).
- 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
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- 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_enter_tree() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.nodes_3d.lights.DirectionalLight3D(**kwargs)[source]¶
Bases:
simvx.core.nodes_3d.lights.Light3DInfinitely distant light: a sun. Every fragment is lit from one direction.
The light has no position and no falloff:
positiononly moves the editor gizmo. The direction is the node’s forward vector, so aim it either by rotating the node or by assigning :attr:direction(which rotates the node for you).rangedoes not apply.With
shadows=Truethe light drives the cascaded shadow maps; the cascade count is set globally byWorldEnvironment.shadow_cascade_count. AWorldEnvironmentwithsky_mode="procedural"also treats the firstDirectionalLight3Din the scene as the sun that generates the sky.Initialization
- gizmo_colour¶
‘Colour(…)’
- direction¶
‘_DirectionProperty(…)’
- get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]¶
Return direction arrow and parallel rays showing light direction.
- colour¶
‘Colour(…)’
- intensity¶
‘Property(…)’
- shadows¶
‘Property(…)’
- light_cull_mask¶
‘Bitmask(…)’
- set_light_cull_mask_layer(index: int, enabled: bool = True) None¶
- is_light_cull_mask_layer_enabled(index: int) bool¶
- 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
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- 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_enter_tree() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.nodes_3d.lights.PointLight3D(**kwargs)[source]¶
Bases:
simvx.core.nodes_3d.lights.Light3DOmnidirectional light at the node’s world position, with a hard range.
Attenuation is the windowed linear falloff
max(1 - distance/range, 0)squared, so brightness reaches exactly zero atrangeand the light contributes nothing beyond it. That cutoff is what lets the renderer cull the light per tile, so keeprangeas tight as the look allows rather than using one huge light. It is not inverse-square: doublingrangestretches the falloff instead of dimming the light, andintensityis the only brightness dial.With
shadows=Truethe light casts a cubemap shadow. The Vulkan backend rendersWorldEnvironment.shadow_caster_countpoint-light shadow cubes per frame (default 1); when more point lights ask than the budget holds, the highest :attr:shadow_prioritywins, then the strongest influence at the camera (intensity * range / distance, so reach counts as much as closeness), and a light already casting keeps its map unless a challenger clearly beats it. Lights past that budget still light the scene, cast nothing, and are reported once. Each caster re-renders every shadow-casting mesh six times per frame, so raise the budget deliberately rather than lighting every lamp.Initialization
- range¶
‘Property(…)’
- shadow_priority¶
‘Property(…)’
- gizmo_colour¶
‘Colour(…)’
- get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]¶
Return 3 circles showing the light range sphere.
- colour¶
‘Colour(…)’
- intensity¶
‘Property(…)’
- shadows¶
‘Property(…)’
- light_cull_mask¶
‘Bitmask(…)’
- set_light_cull_mask_layer(index: int, enabled: bool = True) None¶
- is_light_cull_mask_layer_enabled(index: int) bool¶
- 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
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- 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_enter_tree() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶
- class simvx.core.nodes_3d.lights.SpotLight3D(**kwargs)[source]¶
Bases:
simvx.core.nodes_3d.lights.Light3DCone-shaped light aimed along the node’s forward vector.
Combines the same distance falloff as :class:
PointLight3Dwith a cone mask. Both cone angles are HALF-angles in degrees, measured from the cone axis to its edge, so the defaultouter_cone=45is a 90-degree-wide beam. Fragments insideinner_coneget the full beam, fragments betweeninner_coneandouter_conefade out linearly in cosine space, and fragments pastouter_coneget nothing. Keepinner_conebelowouter_cone: the gap between them is the soft edge, and narrowing it towards zero sharpens the beam.Aim the light by rotating the node (the beam follows
forward); thepositionis the cone apex.With
shadows=Truethe light casts a projected shadow map. The Vulkan backend rendersWorldEnvironment.shadow_caster_countspot shadows per frame (default 1); when more spot lights ask than the budget holds, the highest :attr:shadow_prioritywins, then the strongest influence at the camera (intensity * range / distance, so reach counts as much as closeness), and a light already casting keeps its map unless a challenger clearly beats it. Lights past that budget still light the scene and cast nothing. The shadow frustum is2 * outer_conewide, so a cone approaching 90 degrees loses shadow precision.Initialization
- range¶
‘Property(…)’
- inner_cone¶
‘Property(…)’
- outer_cone¶
‘Property(…)’
- shadow_priority¶
‘Property(…)’
- gizmo_colour¶
‘Colour(…)’
- get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]¶
Return cone wireframe showing spot light direction and angle.
- colour¶
‘Colour(…)’
- intensity¶
‘Property(…)’
- shadows¶
‘Property(…)’
- light_cull_mask¶
‘Bitmask(…)’
- set_light_cull_mask_layer(index: int, enabled: bool = True) None¶
- is_light_cull_mask_layer_enabled(index: int) bool¶
- 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
- script_error_raised¶
‘Signal(…)’
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- 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_enter_tree() None¶
- on_exit_tree() None¶
- on_update(dt: float) None¶
- on_fixed_update(dt: float) None¶
- on_draw(renderer) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶