simvx.core.nodes_3d.mesh

MeshInstance3D – visible 3D mesh node.

Module Contents

Classes

MeshInstance3D

Visible 3D object. Holds a Mesh and Material for the renderer.

Data

log

API

simvx.core.nodes_3d.mesh.log

‘getLogger(…)’

class simvx.core.nodes_3d.mesh.MeshInstance3D(mesh=None, material=None, skeleton=None, **kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

Visible 3D object. Holds a Mesh and Material for the renderer.

Set skeleton to a :class:~simvx.core.skeleton.Skeleton to enable GPU vertex skinning: the renderer reads skeleton.joint_matrices each frame and uploads them as the instance’s bone palette. Skinning needs both halves – the matching joint/weight stream on the mesh and bones on the skeleton, which is what import_gltf builds for a rigged asset. With either half missing there is nothing to pose the vertices with, so the instance is drawn rigidly and the renderer warns once, naming the node and the missing half.

The pivot Property controls where the mesh’s local origin sits relative to its bounding box. "center" (default) treats the mesh-local origin as the geometric centre: convenient for free- floating objects. "bottom" lifts the mesh by half its height along local +Y, so foot-aligned entities (characters, props, obstacles) place naturally on a ground plane at local_position.y = 0. The shift is applied at draw time via the model matrix; the mesh data itself is untouched.

Usage: from simvx.core.graphics.mesh import Mesh from simvx.core.graphics.material import Material mi = MeshInstance3D(mesh=Mesh.cube(), material=Material(colour=(1, 0, 0)))

# Foot-aligned cube: position.y = 0 puts the bottom on the floor:
mi = MeshInstance3D(mesh=Mesh.cube(), pivot="bottom")

# Skeletal mesh:
mi.skeleton = skeleton_node

Initialization

lod_bias

‘Property(…)’

pivot

‘Property(…)’

skeleton_path

‘_SkeletonPathProperty(…)’

property skeleton[source]

Skeleton posing this mesh, or None for a rigid mesh.

Accepts a :class:~simvx.core.skeleton.Skeleton (or None to turn skinning off). Assigning one does not reparent it and does not require it to be in the tree: the renderer only reads its joint_matrices, so a skeleton driven by an AnimationPlayer poses the mesh wherever it lives.

What the scene file carries is :attr:skeleton_path, because a node reference has no source form. A skeleton that shares a scene with this mesh survives a save; one that lives outside the mesh’s tree does not, and that scene reloads unrigged. The runtime promise above is unchanged: only the trip through a file is narrower.

Skinning needs both halves: per-vertex joints and weights on the mesh, and bones on the skeleton. With either one missing the skeleton is ignored – a bone-less Skeleton() no more skins the mesh than an unrigged mesh does – and the instance renders rigidly, exactly as it would with no skeleton, while the renderer logs one warning naming this node and the missing half. The assignment itself stands: the fallback is a draw decision, so reading skeleton back returns what was set.

property model_matrix: numpy.ndarray[source]

Model transform matrix from global position/rotation/scale.

When pivot == "bottom", the mesh is pre-translated along its local +Y by half its bounding-box height so the bottom of the mesh sits at local_position.y. The shift is applied in the node’s local frame so it rotates / scales with the node.

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__()