| Header | Link |
|---|---|
| Purpose | Purpose |
| Use Cases | Use Cases |
| Practical Example | Practical Example |
| Reference | Reference |
| 3D Mesh Flips | 3D Mesh Flips |
| Material Param Overrides | Material Param Overrides |
Nodes are the building blocks of every Perro scene β data-only objects that the runtime renders and simulates. This page catalogs the built-in node types (2D, 3D, UI, resource, and skeletal) and the fields each one exposes, so you know what to place in a scene, what a script can read and write, and how rendering, physics, and resources hang off them.
- Move, rotate, or hide game objects from a script: read and write
Node2D/Node3Dtransforms withget_local_pos_3d!/set_local_pos_3d!, and edit other fields throughwith_node_mut!. - Show art and text:
Sprite2D/Sprite3D,AnimatedSprite2D,Label2D/Label3D,MeshInstance3D,MultiMeshInstance3D, andDecal3D. - Control what a camera sees and how it looks:
Camera2D/Camera3Dwithrender_mask, post-processing, and modulate tints. - Drive skeletal characters:
Skeleton3D/Skeleton2D, bones,BoneAttachment*, and blend shapes for morph/facial animation. - Fade or tint an object and its children: the
modulate/self_modulate/children_modulateRGBA multipliers. - Inspect a node's identity and relationships at runtime:
get_node_type!,get_node_name!,get_node_tags!,get_node_children_ids!.
A node owns engine-facing data such as transform, render, physics, or UI fields. Its script owns per-instance gameplay state and reaches the attached node through ctx.id. Store a fixed other node as NodeID; derive a true parent/child dependency from structure; query only a changing set. Pick the narrowest node type that owns the behavior, then use base-node helpers only for fields shared across types.
Do not use runtime names as dependency injection. Names help people read scenes; NodeID values preserve the actual target across duplicate names and scene composition.
A side-scroller sprite that walks right and faces the direction it moves. It edits the Sprite2D node's flip_x field through with_node_mut!.
use perro_api::prelude::*;
lifecycle!({
fn on_update(&self, ctx: &mut ScriptContext<'_, API>) {
let dt = delta_time!(ctx.run);
let vx = 60.0 * dt;
if let Some(pos) = get_local_pos_2d!(ctx.run, ctx.id) {
set_local_pos_2d!(ctx.run, ctx.id, pos + Vector2::new(vx, 0.0));
let _ = with_node_mut!(ctx.run, Sprite2D, ctx.id, |sprite| {
sprite.flip_x = vx < 0.0;
});
}
}
});This page lists the built-in node types and their purpose. Nodes store data-only state.
Rendering and resource loading are handled by the runtime and ResourceWindow.
Node
- Generic non-spatial scene node.
- Use it as a script/root grouping node when no 2D, 3D, UI, or resource data is needed.
Webcam
- Live webcam capture source for camera stream nodes.
- Does not draw by itself.
- Use it as
CameraStream2D.camera,CameraStream3D.camera, orUiCameraStream.camera. slotis the device slot string. Empty uses the default device at index0.resolution,width,height,fps,mirror,cpu_frames, andenabledconfigure capture.- The stream path opens capture automatically while the referenced
Webcamnode is enabled and visible. - The stream path closes capture when the node is disabled, hidden, or no longer referenced.
- Use
ctx.res.Webcams().devices()to list connected devices and avoid directnokhwause. - See Webcam Module.
Node2D
- Base transform for 2D nodes (position, rotation, scale, z_index, visible).
render_layersusesBitMask. A renderable node draws when it does not intersect the activeCamera2D.render_mask.- Default
render_layersis all layers. modulate,self_modulate, andchildren_modulateare RGBA multipliers.modulateaffects self and descendants;self_modulateaffects only self;children_modulateaffects only descendants.- Effective draw color is parent child modulate * node modulate * node self modulate. All fields default to white.
Sprite2D
- Renders a textured quad.
- Holds a
TextureID(not raw pixels). UseTexturemodule to load. texture_regionselects an atlas rect.
Label2D
- Renders world-space text through the UI text renderer.
- Uses
Node2Dposition, rotation, scale, z index, visibility, render layers, and modulation. sizeis in 2D world units before camera projection.- Uses
text,color,font_size,h_align, andv_alignlikeUiLabel. - Uses
font = "res://fonts/Game.ttf"orfont = "system://Segoe UI"; missing fonts fall back. - Supports
%loc:scene text markers and runtime locale text binding likeUiLabel. - Use it for nameplates, speech text, signs, and diegetic UI.
Button2D
- Clickable world-space rect.
- Uses
Node2Dposition, rotation, scale, z index, visibility, and render layers. - Holds
size, normal/hover/pressed fills, input state, cursor icon, and extra button signal lists. - Uses pointer cursor by default on hover. Set
cursor_iconorhover_cursor_iconto override. - Emits default
<node_name>_<event>signals for hover enter, hover exit, pressed, released, and clicked. *_signalsfields add extra signals; they do not replace the default named signal.- Hit testing uses the active 2D camera and world mouse position.
ImageButton2D
- Clickable world-space image.
- Uses
Node2Dtransform fields likeButton2D. - Holds
texture,texture_region,size, normal/hover/pressed tint, input state, cursor icon, and extra button signal lists. - Uses pointer cursor by default on hover. Set
cursor_iconorhover_cursor_iconto override. - Emits the same default
<node_name>_<event>signals asButton2D. *_signalsfields add extra signals; they do not replace the default named signal.- Use it for sprite-like buttons, diegetic UI, and world-space interact prompts.
NineSlice2D
- Scalable world-space texture panel.
- Uses
texture,texture_region,size,margins, andtint. - Zero default
marginsauto-split the texture or region into equal thirds and scale all nine parts to fit. - Keeps corners fixed while stretching edges and center.
- Use it as a frame, speech bubble, health bar, or child/background near
Button2Dnodes.
AnimatedSprite2D
- Renders a sprite sheet.
- Uses normal
texture = "res://..."likeSprite2D. animationslists named strip/grid layouts withframe_size,frame_count,columns, andfps.- Uses
current_animation,current_frame,fps_scale,playing, andlooping. - Advances during internal update and renders the current atlas frame.
- Omit
columnsfor a left-to-right strip. - Set
columnswhen frames wrap to more than one row in a grid.
WaterBody2D
- Shaped water surface centered on its
Node2Dposition. - Supports quad/rect and circle bounds through
shape. - Renders through the retained 2D water path when visible and not hidden by camera
render_mask. - Runs GPU height/foam simulation with idle modes, wind, flow, damping, wake, foam, camera-distance LOD, and sample readback controls.
- Applies camera-distance-LOD fixed-step buoyancy and vertical drag to
RigidBody2Dwhen body centers are inside the water shape and below sampled surface height. - Uses
RigidBody2D.densityfor buoyancy scale. - Does not create collision shapes, raycast hits, contacts, or area signals by itself.
- Add
StaticBody2D,Area2D, orCollisionShape2Dnodes separately for solid banks, floors, triggers, or queries. - See Water Bodies.
Camera2D
- Active 2D camera (position/rotation/zoom).
render_maskhides matchingrender_layerson 2D renderable nodes.- Default
render_maskis no layers (BitMask::NONE), so the camera hides nothing. - Supports camera post-processing via
post_processing(see "Camera Post-Processing" below). - Supports listener audio effects via
audio_options;audio_maskignores matching emittedaudio_layer.
CameraStream2D
- Draws an explicitly referenced camera from the existing world into 2D space.
- Use it when the source camera and its content physically belong to the unified world.
- Uses transparent pixels where the source camera renders no object.
- Tone-maps camera-rendered HDR once and preserves premultiplied alpha edges.
- Applies source-camera post-processing before stream post-processing.
SubView2D
- Owns an isolated mixed 2D/3D descendant scope.
- Draws that scope onto a 2D quad through implicit 2D and 3D views.
- Uses active descendant
Camera2DandCamera3Dnodes for their matching local views. - Keeps each implicit view as the fallback when no matching local camera is active.
- Keeps owned descendants out of main-world cameras and camera streams.
- Use it for a 3D model that behaves like one 2D game object.
- Host transform and render layers affect the output quad, not local child camera space.
- Defaults to a transparent clear and premultiplied HDR output.
- Uses
size,resolution,view_*,view_2d_*,projection,background,tint, andpost_processing.
2D lights:
AmbientLight2DRayLight2DPointLight2DSpotLight2D- Uses
color,intensity,cast_shadows, andactive. PointLight2DandSpotLight2Duserange.SpotLight2Dusesinner_angle_radiansandouter_angle_radians.shadow_softness(0..1) andshadow_samples(1..16) add soft penumbra.- Visible collision shapes and enabled tilemap collision tiles cast shadows.
- See 2D Shadows.
Skeleton2D
- 2D rig root.
- Owns
Vec<Bone2D>data likeSkeleton3D. - Load bones with
skeleton = "res://rig.pskel2d". - Use
BoneAttachment2D,IKTarget2D, andPhysicsBoneChain2Dnodes to target bones.
Bone2D
- Data-only bone inside
Skeleton2D.bones. - Fields:
name,parent,rest,pose,inv_bind. .panimbone tracks animateposewithTransform2D.
Physics 2D:
CollisionShape2DStaticBody2DRigidBody2DCharacterBody2DArea2DCollisionShape2Dshould be authored as a child ofStaticBody2D,RigidBody2D,CharacterBody2D, orArea2D.CharacterBody2Dis a script-driven kinematic body: engine applies gravity with a collision sweep, no velocity/force state. See Physics Nodes.- Static/rigid bodies and areas participate in audio propagation by default through
audio_interaction. - Collision shapes only provide geometry.
- See Physics Nodes for scene authoring examples.
2D physics shape authoring:
[Body]
[RigidBody2D]
[Node2D/]
[/RigidBody2D]
[/Body]
[BodyShape]
parent = @Body
[CollisionShape2D]
shape = { type = quad width = 1.0 height = 1.0 }
[/CollisionShape2D]
[/BodyShape]
2D physics layer/mask fields:
collision_layers: BitMaskcollision_mask: BitMask- Default
collision_layersis all layers. - Default
collision_maskis no layers. collision_layerstags the collider;collision_masklists tags to ignore.- Colliders interact only when neither collider's mask ignores the other collider's layers.
- Scene files use
collision_layers = [1, 2]andcollision_mask = [3]; Rust code should useBitMask::with([1, 2]).
2D joint nodes:
PinJoint2DDistanceJoint2DFixedJoint2D
Joint common fields are body_a, body_b, anchor_a, anchor_b, enabled, and collide_connected.
DistanceJoint2D also uses min_distance and max_distance.
Anchors are local to each connected body.
Audio 2D:
AudioMask2DAudioEffectZone2DAudioPortal2DAudioMask2Dis invisible audio-only geometry withCollisionShape2Dchildren.AudioEffectZone2Dstores ordered reverb/echo/dampening effects.audio_maskignores matching emittedaudio_layer; shape overlap with source/listener/path applies the chain.AudioPortal2Dmarks one-way inputs withCollisionShape2Dchildren and linked portal exits. Hit point and ray direction transform through target portal global transforms, then continue through portal hits or physics bounces. Immediate re-entry into the portal just exited is blocked until another portal hit or physics bounce.- See Audio Nodes for scene authoring examples.
2D audio shape authoring:
[Zone]
[AudioEffectZone2D]
active = true
effects = [{ reverb_send = 0.35 echo = 0.0 dampening = 0.0 }]
[Node2D/]
[/AudioEffectZone2D]
[/Zone]
[ZoneShape]
parent = @Zone
[CollisionShape2D]
shape = { type = quad width = 4.0 height = 4.0 }
[/CollisionShape2D]
[/ZoneShape]
TileMap2D is the tile map node.
It uses .ptileset data and can emit static 2D colliders from collision_shape = "auto".
See TileMap2D.
Node3D
- Base transform for 3D nodes (position, rotation, scale, visible).
render_layersusesBitMask. A renderable node draws when it does not intersect the activeCamera3D.render_mask.- Default
render_layersis all layers. modulate,self_modulate, andchildren_modulatework likeNode2D, multiplying rendered colors and material surface modulate.
MeshInstance3D
- Renders a mesh with per-surface material bindings.
- Holds
MeshIDandsurfaces: Vec<MeshSurfaceBinding>instead of raw mesh/material data. - Reason: resource IDs allow caching, reuse, and async GPU upload.
MeshSurfaceBindingsupports:material: Option<MaterialID>modulate(RGBA multiplier)overrides(named material parameter overrides)- flat/smooth override names:
flat_shading/flatShading,shade_flat/shadeFlat,shade_smooth/shadeSmooth
- Scene authoring supports
surfaces = [ ... ]where each entry can be:- a material source string
- an object with
material,modulate, andoverrides
- Legacy
material = ...is still accepted and maps to surface index0. - Skinning: if the mesh has vertex weights, it can be deformed by a Skeleton3D.
- Runtime link:
skeleton: NodeIDpoints to theSkeleton3Dnode that supplies bone transforms. - Scene authoring:
skeleton = @NodeNameuses the scene node key and is resolved to aNodeIDat load time. - Skinning only works if the mesh has proper vertex weights (
JOINTS_0/WEIGHTS_0). - Shared-skeleton mesh reuse works when meshes follow the same rig contract: same joint order/indices and compatible weights.
- Mismatched rigs can use
.pretargetalias/rest maps during glTF import or static.panimbake. - glTF morph targets import as blend shapes.
- glTF
TEXCOORD_1imports as dedicated paint UVs. Meshes without UV1 use UV0 as paint UV fallback. blend_shape_weightsstores indexed blend shape weights in Blender-style0.0..1.0.- Aliases:
shape_key_weights,morph_weights. - Overflow weights are ignored. Missing weights act as
0.0. - Mesh LOD is automatic for authored meshes.
- Dynamic/dev load builds LODs on load; static build packs render meshes into
.pmeshv2. - Meshes with joints/weights skip LOD generation.
- Surface/material slots stay stable across LODs.
- Current switch distances are radius-scaled (
36x,54x,72x,108x,144xmesh bounds radius). lodstoresLODOptionswithmin_lod/max_lod.- LOD values are quality levels, not baked index numbers.
LODOptions::MAX = 5is most detail (100%, baked LOD0).LODOptions::MIN = 0is least detail (12.5%, last baked LOD when present).- Middle names:
LOW = 1,MEDIUM_LOW = 2,MEDIUM = 3,HIGH = 4. - Scene authoring supports
min_lod/max_lodpluslod_min/lod_maxaliases. - Defaults keep full auto range:
min_lod = LODOptions::MIN,max_lod = LODOptions::MAX. flip_x,flip_y, andflip_zmirror the rendered mesh around the node's local origin/pivot.- Use mesh flips when a part needs to become its real opposite-side shape without making a second mesh resource.
- This is different from rotation. A rotated left hair part is still shaped like the left-side mesh. A flipped left hair part becomes the right-side mirror.
- This is also different from "inside out". The renderer handles mirrored winding/flat normals so the mesh remains renderable as a proper mirror.
MultiMeshInstance3D
- Renders many copies of one mesh.
- Uses shared mesh/material surface bindings.
instancesstores per-instance transform: position, rotation, and scale.instance_gridcan emit per-instance scale viascaleandscale_wave.- Use it for repeated static props, foliage, debris, or crowd-like non-skinned copies.
- Supports same LOD clamp fields as
MeshInstance3D. - Supports
flip_x,flip_y, andflip_zon the whole multimesh node. blend_shape_weightsis the default blend shape weight array for every dense instance.- Per-instance
blend_shape_weightsinsideinstancesoverrides the node default for that instance.
Sprite3D
- Renders a floating textured rect in 3D space.
- Uses
Node3Dposition, rotation, scale, visibility, render layers, and modulation. sizeis world-space width/height before camera projection.- Holds a
TextureIDthroughtexture;texture_regionselects an atlas rect. - Supports
flip_xandflip_y. - Unlike
Decal3D, it does not project onto geometry. It can float in front of or above meshes. - Use it for pickups, markers, billboards, damage icons, and world prompts.
Label3D
- Renders floating text in 3D space through the UI text renderer.
- Uses
Node3Dposition, rotation, scale, visibility, render layers, and modulation. sizeis world-space width/height before camera projection.- Uses
text,color,font_size,h_align, andv_alignlikeUiLabel. lock_orientation = falsekeeps text camera-facing. Set it totruefor a fixed world sign: glyph and backdrop meshes project through all four transformed corners without rasterizing the whole label;backface_culldefaults totrueand hides the rear face.visible_through_objects = falseis the default and depth-tests each label pixel against scene geometry. Set it totruefor an always-visible marker or nameplate.backdrop_colordefaults transparent. Set it plussizeto make a filled sign behind the text.padding = (left, top, right, bottom)uses the UI ratio convention and insets text inside the sign.corner_radii = (tl, tr, br, bl)uses the UI0.0..1.0ratio convention. Default0gives hard square corners.- Text wrapping uses the padded authored
sizeaspect andfont_size, so camera angle/distance does not change line breaks. - Supports
%loc:scene text markers and runtime locale text binding likeUiLabel. - Use it for nameplates, signs, speech text, and world HUD labels.
Camera3D
- Active 3D camera with projection settings.
render_maskhides matchingrender_layerson 3D renderable nodes.- Default
render_maskis no layers (BitMask::NONE), so the camera hides nothing. - Supports camera post-processing via
post_processing(see "Camera Post-Processing" below). - Supports listener audio effects via
audio_options;audio_maskignores matching emittedaudio_layer. - Perspective, orthographic, and frustum projections expose near and far clip planes.
CameraStream3D
- Draws an explicitly referenced camera from the existing world onto a 3D quad.
- Use it for physical in-game cameras such as monitors, mirrors, portals, and CCTV feeds.
- Uses transparent pixels where projection or near/far clipping leaves no object.
- Omits the visual
Sky3Dbackground while retaining sky lighting and environment effects. - Tone-maps camera-rendered HDR once and preserves premultiplied alpha edges.
- Applies source-camera post-processing before stream post-processing.
SubView3D
- Owns an isolated mixed 2D/3D descendant scope.
- Draws that scope onto a 3D quad through implicit 2D and 3D views.
- Uses active descendant
Camera2DandCamera3Dnodes for their matching local views. - Keeps each implicit view as the fallback when no matching local camera is active.
- Keeps owned descendants out of main-world cameras and camera streams.
- Use it for a local 2D panel, effect, or miniature scene embedded in 3D.
- Host transform and render layers affect the output quad, not local child camera space.
- Defaults to a transparent clear and premultiplied HDR output.
- Uses
size,resolution,view_*,view_2d_*,projection,background,tint, andpost_processing.
ParticleEmitter3D
- 3D particle emitter driven by a particle profile.
WaterBody3D
- Shaped water surface centered on its
Node3Dposition. - Uses local X/Z as surface axes and world Y as height.
- Supports box and cylinder bounds through
shape. - Renders through the 3D water path when visible and not hidden by camera
render_mask. - Runs GPU height/foam simulation with idle modes, wind, flow, damping, wake, foam, camera-distance LOD, and sample readback controls.
- Applies camera-distance-LOD fixed-step buoyancy and vertical drag to
RigidBody3Dwhen body centers are inside the water shape and below sampled surface height. - Uses
RigidBody3D.densityfor buoyancy scale. - Does not create collision shapes, raycast hits, contacts, or area signals by itself.
- Add
StaticBody3D,Area3D, orCollisionShape3Dnodes separately for lake beds, shores, triggers, or queries. - See Water Bodies.
Decal3D
- Projected box decal: paints albedo/normal/emission onto lit 3D geometry inside its
sizebox. - Projects along local -Z (rotate the node like a spotlight to aim it).
- Applied in the material shaders before lighting, so decals receive shadows and lights like the surface under them.
albedo_texture,normal_texture,emission_textureare all optional; with no albedo texture themodulatecolor paints flat.albedo_mixblends decal albedo over the surface;normal_strengthscales the normal patch.normal_fade(0..1) rejects surfaces facing away from the projection axis.distance_fade_begin/distance_fade_lengthfade by camera distance; begin0disables.- Higher
sort_priorityblends over lower when decals overlap. - Affects standard and toon materials plus multimesh instances; unlit materials ignore decals.
ParticleEmitter2D
- 2D particle emitter driven by a particle profile.
- Reads
.ppartxandy; ignoresz.
AnimationPlayer
- Plays an
AnimationClipresource and applies tracks to bound scene nodes. animationpoints to clip source/ID; tracks are mapped by object name viabindings.playbacksupportsonce,loop,boomerang.- Runtime control is exposed through Animation macros in Runtime API.
Lights:
AmbientLight3DSky3DRayLight3DPointLight3DSpotLight3D- Shadow-casting 3D lights use
cast_shadows,shadow_strength,shadow_depth_bias, andshadow_normal_bias. See 3D Shadows.
Physics 3D:
CollisionShape3DStaticBody3DRigidBody3DCharacterBody3DArea3DCollisionShape3Dshould be authored as a child ofStaticBody3D,RigidBody3D,CharacterBody3D, orArea3D.CharacterBody3Dis a script-driven kinematic body: engine applies gravity with a collision sweep, no velocity/force state. See Physics Nodes.CollisionShape3Dsupports primitiveshapeand mesh-backedtrimeshsource.flip_x,flip_y, andflip_zmirror collision geometry around local origin.- Trimesh source format:
res://path/to/model.glb:mesh[0](mesh index optional, default0). - Static/rigid bodies and areas participate in audio propagation by default through
audio_interaction. - Collision shapes only provide geometry.
- See Physics Nodes for scene authoring examples.
3D physics shape authoring:
[Body]
[StaticBody3D]
[Node3D/]
[/StaticBody3D]
[/Body]
[BodyShape]
parent = @Body
[CollisionShape3D]
shape = { type = cube, size = (1, 1, 1) }
[/CollisionShape3D]
[/BodyShape]
Use flip_x, flip_y, and flip_z when one asymmetric mesh should draw as its mirrored counterpart.
Common cases:
- Character creator hair, horns, shoulder pads, pockets, belt pouches, or earrings.
- Left/right accessory variants where rotation does not create the correct shape.
- Modular level pieces where one mesh needs mirrored layout variation.
- Runtime customization where many nodes reuse one
MeshID.
Do not use flip for:
- Implicit physics collision mirror. Collision shapes are separate nodes and need their own
flip_x,flip_y, orflip_z. - Author-time mesh baking. Flip is per node render state.
- Cases where rotating the object is actually the intended result.
Scene example:
[HairLeft]
parent = @Character
[MeshInstance3D]
mesh = "res://models/hair_part.glb:mesh[0]"
flip_x = false
[Node3D]
position = (-0.18, 1.72, 0.04)
[/Node3D]
[/MeshInstance3D]
[/HairLeft]
[HairRight]
parent = @Character
[MeshInstance3D]
mesh = "res://models/hair_part.glb:mesh[0]"
flip_x = true
[Node3D]
position = (0.18, 1.72, 0.04)
[/Node3D]
[/MeshInstance3D]
[/HairRight]
Script example:
methods!({
fn set_hair_side(&self, ctx: &mut ScriptContext<'_, API>, hair: NodeID, right_side: bool) {
with_node_mut!(ctx.run, MeshInstance3D, hair, |mesh| {
mesh.flip_x = right_side;
});
}
});Character creator example:
methods!({
fn equip_accessory(
&self,
ctx: &mut ScriptContext<'_, API>,
accessory: NodeID,
mesh_id: MeshID,
mirror_x: bool,
) {
with_node_mut!(ctx.run, MeshInstance3D, accessory, |node| {
node.mesh = mesh_id;
node.flip_x = mirror_x;
node.flip_y = false;
node.flip_z = false;
});
}
});Level variation example:
with_node_mut!(ctx.run, MeshInstance3D, wall_trim, |node| {
node.flip_z = variation_index & 1 != 0;
});Behavior notes:
- Flip mirrors around local origin/pivot after the node transform is chosen.
- Multiple axes can be enabled at once.
- Odd-axis flips reverse triangle winding internally for render batching.
- Materials still use the same mesh and surface bindings.
MeshInstance3DandMultiMeshInstance3Dshare the same field names.- For skinned meshes, flip mirrors the rendered skinned result at the mesh node level.
CollisionShape3Dalso accepts these fields, but mesh render flip does not affect collision shape flip.
MeshInstance3D and MultiMeshInstance3D can hold their own value for a custom
shader param while still sharing the base material. The node keeps the override;
the material is untouched, so no extra material, bind group, or uniform is
created per node.
// Surface 0 by default.
set_node_material_param!(ctx, MeshInstance3D, id, "glow", 0.7);
// Explicit surface index.
set_node_material_param!(ctx, MultiMeshInstance3D, id, 1, "glow", 0.7);
// Fall back to the material's own value.
clear_node_material_param!(ctx, MeshInstance3D, id, "glow");Values accept f32, i32, bool, [f32; 2], [f32; 3], and [f32; 4].
Both macros return Option<bool>: None when the node is missing or is not that
type, Some(false) when the stored value did not change. Overrides are keyed by
name, so setting the same name again replaces it rather than appending, and
re-asserting a value costs nothing. That makes these safe to call every frame.
| Situation | Use |
|---|---|
| One value shared by every surface using the material | set_material_param! |
| A value that differs per node, over a shared material | set_node_material_param! |
| A value that differs per instance inside one multimesh | Neither yet; overrides are per surface, not per instance |
Do not give each node its own material just to vary one number. That is a material, bind group, and uniform per node; an override is none of those.
Scene files can author the same overrides, so a script override and a .scn
override target the same slot.
Perro imports glTF morph targets as blend shapes.
Weights use Blender-style 0.0..1.0 values.
Weights are applied by array index.
Overflow entries are ignored.
Missing entries behave as 0.0.
Weights are clamped to 0.0..1.0.
Weights are not normalized across targets.
Scene defaults:
[Face]
[MeshInstance3D]
mesh = "res://characters/face.glb:mesh[0]"
blend_shape_weights = [0.0, 0.2, 0.1]
[/MeshInstance3D]
[/Face]
Aliases:
shape_key_weights = [0.0, 0.2, 0.1]
morph_weights = [0.0, 0.2, 0.1]
MultiMesh defaults and per-instance overrides:
[Crowd]
[MultiMeshInstance3D]
mesh = "res://characters/face.glb:mesh[0]"
blend_shape_weights = [0.25, 0.0, 0.5]
instances = [
{ position = (0, 0, 0) },
{ position = (2, 0, 0), scale = (1.2, 1.0, 1.2), blend_shape_weights = [1.0, 0.2] },
]
[/MultiMeshInstance3D]
[/Crowd]
Runtime mutation:
with_node_mut!(ctx.run, MeshInstance3D, face_id, |node| {
node.blend_shape_weights.resize(3, 0.0);
node.blend_shape_weights[1] = 0.75;
});Runtime MultiMesh mutation:
with_node_mut!(ctx.run, MultiMeshInstance3D, crowd_id, |node| {
node.blend_shape_weights = vec![0.2, 0.0];
node.instances[4].blend_shape_weights = Some(vec![1.0, 0.5]);
});3D physics layer/mask fields:
collision_layers: BitMaskcollision_mask: BitMask- Default
collision_layersis all layers. - Default
collision_maskis no layers. collision_layerstags the collider;collision_masklists tags to ignore.- Colliders interact only when neither collider's mask ignores the other collider's layers.
- Scene files use
collision_layers = [1, 2]andcollision_mask = [3]; Rust code should useBitMask::with([1, 2]).
3D joint nodes:
BallJoint3DHingeJoint3DFixedJoint3D
Joint common fields are body_a, body_b, anchor_a, anchor_b, enabled, and collide_connected.
HingeJoint3D also uses axis.
Anchors are local to each connected body.
Audio 3D:
AudioMask3DAudioEffectZone3DAudioPortal3DAudioMask3Dis invisible audio-only geometry withCollisionShape3Dchildren.AudioEffectZone3Dstores ordered reverb/echo/dampening effects.audio_maskignores matching emittedaudio_layer; shape overlap with source/listener/path applies the chain.AudioPortal3Dmarks one-way inputs withCollisionShape3Dchildren and linked portal exits. Hit point and ray direction transform through target portal global transforms, then continue through portal hits or physics bounces. Immediate re-entry into the portal just exited is blocked until another portal hit or physics bounce.- See Audio Nodes for scene authoring examples.
3D audio shape authoring:
[AudioWall]
[AudioMask3D]
active = true
[Node3D/]
[/AudioMask3D]
[/AudioWall]
[AudioWallShape]
parent = @AudioWall
[CollisionShape3D]
shape = { type = cube, size = (1, 2, 0.2) }
[/CollisionShape3D]
[/AudioWallShape]
Skeleton3D
- Holds
Vec<Bone3D>(data-only). - Bones are loaded via
ResourceWindow::Skeletons().load_bones(source). - Typical flow: scene specifies a
skeletonpath, and scene loader fillsbones.
BoneAttachment3D
- Follows one bone on a
Skeleton3D. - Fields:
skeleton:@SkeletonNodeKeyref.boneorbone_index: zero-based index intoSkeleton3D.bones.
- Runtime resolves
skeleton = @NodeNameto aNodeIDat load time. - Each internal update computes skeleton global transform + bone pose chain transform.
- Attachment's global 3D transform is set to that bone transform.
- Children of the attachment inherit that transform.
- Use it for held gear, muzzle flashes, hit markers, socketed VFX, or any node that should follow a bone.
PhysicsBoneChain3D
- Simulates one bone chain on a
Skeleton3Dduring fixed update. - Scene fields:
skeleton:@SkeletonNodeKeyref.boneorbone_index: zero-based end bone index.chain_length: number of links back from end bone.gravity: world-space acceleration.damping,stiffness,radius,collisions,iterations.
iterationsdefault is3; use2for fast chains or4for slower quality chains.- Uses Verlet-style points, pins the root of the selected chain, writes solved positions back into bone
pose. - Reacts to skeleton movement because simulation state is kept in world space.
BoneCollider3D
- Static collider source for
PhysicsBoneChain3D. - Add
CollisionShape3Dchildren, likeStaticBody3D. - Chain collisions support all
CollisionShape3Dchild shapes. - Primitive shapes use local shape pushout;
TriMeshuses a conservative node-space sphere fallback.
IKTarget3D
- Solves a CCD IK chain on one
Skeleton3D. - Fields:
skeleton:@SkeletonNodeKeyref.boneorbone_index: zero-based end bone index.chain_length: parent chain length to solve.iterations: CCD pass count.tolerance: stop distance in skeleton-local units.weight: solve blend0..1.match_rotation: match target rotation on end bone.
- Writes solved transforms into bone
pose; keeps bonerestunchanged.
UI nodes inherit from UiNode in the node registry:
UiNodeUiPanelUiButtonUiImageUiImageButtonUiNineSliceUiAnimatedImageUiSubViewUiLabelUiTextBoxUiTextBlockUiLayoutUiHLayoutUiVLayoutUiGridUiTreeList
UI positions and sizes resolve against the parent UI rect.
Root UI nodes use the virtual viewport as parent.
Each axis can be pixels or ratio, so UiVector2::ratio(0.5, 0.5) means parent center.
All UI nodes can have children.
UiNode is the invisible generic container.
UiPanel draws a styled box.
UiButton draws an interactive styled box and emits default named signals plus extra configured signals.
UiImage draws a texture region with tint and scale mode.
UiImageButton draws an interactive texture region and emits default named signals plus extra configured signals.
UiNineSlice auto-splits its texture into thirds with fixed corners and tiled edges/center.
Custom nonzero margins override the split.
UiAnimatedImage draws sprite-sheet animation in UI space.
UiSubView renders isolated local 2D and 3D descendants inside its UI bounds with implicit views and no required camera node.
An active descendant camera replaces the matching implicit 2D or 3D view.
SubView2D renders the same mixed child scope onto a 2D world quad.
SubView3D renders the same mixed child scope onto a 3D world quad.
The suffix names the output host space, not the allowed child dimension.
Camera streams show explicitly referenced cameras from the existing world; SubViews own local render scopes whose descendants stay hidden from main-world cameras and streams.
Legacy UiViewport scene names remain a load alias for UiSubView.
UiLabel draws text.
Use Label2D or Label3D for world-space text that still uses UiLabel text, alignment, and locale binding fields.
UiTextBox edits one line of text.
UiTextBlock edits multi-line text.
UiLayout, UiHLayout, UiVLayout, and UiGrid add automatic child placement.
UiTreeList renders nested data rows from node-owned item data.
See UI Nodes.
Buttons emit default <node_name>_<event> signals.
hover_signals, hover_exit_signals, pressed_signals, released_signals, and clicked_signals add extra signals on top of the default named signal.
UI style resources:
- Inline
style = { ... }stays valid. - Resource
style = "res://ui/panel.uistyle"loads the sameUiStyleschema. - Button
hover/pressedand text editfocused_styleaccept.uistylepaths. .uistyleis visual-only; layout stays on UI nodes.
For copy/paste scene node authoring templates (with all exposed fields, including nil/empty-default fields), see:
Post-processing is configured per camera using post_processing.
See docs/resources/postprocess.md for full details and examples.
Visual accessibility is configured globally through ResourceWindow (not per-camera).
It runs after camera and global post-processing as the final pass.
See Visual Accessibility.
Bone3D fields:
name: bone nameparent: parent bone index (-1for root)rest: rest transform (local)inv_bind: inverse bind transform
Skeleton2D is a Node2D container that owns Vec<Bone2D>.
Bones are data, not scene nodes.
This mirrors Skeleton3D.
Scene:
[Rig2D]
[Skeleton2D]
skeleton = "res://rigs/hero.pskel2d"
[/Skeleton2D]
[/Rig2D]
[HandMarker]
parent = @Rig2D
[BoneAttachment2D]
skeleton = @Rig2D
bone = 1
[/BoneAttachment2D]
[/HandMarker]
Animation:
[Objects]
Rig2D = Skeleton2D
[/Objects]
[Frame0]
@Rig2D {
bone["UpperArm"].rotation = 0.0
}
[/Frame0]
[Frame12]
@Rig2D {
bone["UpperArm"].rotation = 0.7
}
[/Frame12]
From scene:
[Rig]
[Skeleton3D]
skeleton = "res://models/rig.gltf:skeleton[0]"
[/Skeleton3D]
[/Rig]
[SkinnedMesh]
[MeshInstance3D]
mesh = "res://models/rig.gltf:mesh[0]"
surfaces = [
{
material = "res://materials/skin.pmat"
modulate = (1, 1, 1, 1)
}
]
skeleton = @Rig
[/MeshInstance3D]
[/SkinnedMesh]
From script:
let bones = skeleton_load_bones!(res, "res://models/rig.gltf:skeleton[0]");
with_node_mut!(ctx.run, Skeleton3D, node_id, |skel| {
skel.bones = bones;
});Skeleton2D and Skeleton3D accept a bones field with per-bone pose
overrides, keyed by bone name:
[Rig]
[Skeleton3D]
skeleton = "res://models/rig.gltf:skeleton[0]"
bones = {
Spine = { position = (0, 1.5, 0), rotation = (0, 0, 0, 1), scale = (1, 1, 1) },
Head = { rotation_deg = (0, 30, 0) }
}
[/Skeleton3D]
[/Rig]
Rules:
- Overrides apply to the bone
poseafter the rig's bones load;reststays untouched. - Only authored components override; unlisted components keep the loaded pose value.
Skeleton3Drotation accepts a quaternion(x, y, z, w)or Euler radians(x, y, z);rotation_degaccepts Euler degrees.Skeleton2Drotation is radians (rotation_degfor degrees).- Bone names that do not exist in the loaded rig are ignored.
- Animation tracks that target a bone overwrite its pose during playback; bones without tracks keep their scene override.
Swapping a mesh's skeleton at runtime (mesh must have vertex weights and match the skeleton rig contract):
with_node_mut!(ctx.run, MeshInstance3D, mesh_id, |mesh| {
mesh.skeleton = new_skeleton_node_id;
});Use BoneAttachment3D when a normal child transform is not enough.
A normal child follows a scene node.
BoneAttachment3D follows a bone inside a Skeleton3D.
The binding is two-part:
skeleton = @CharacterSkeletonbinds to the skeleton scene node.bone = 15binds to bone index15insideSkeleton3D.bones.
Then parent child nodes under the attachment. The child can still have local offset/rotation/scale. Example: sword in hand.
[Character]
[Node3D]
position = (0, 0, 0)
[/Node3D]
[/Character]
[CharacterSkeleton]
parent = @Character
[Skeleton3D]
skeleton = "res://characters/knight.glb:skeleton[0]"
[/Skeleton3D]
[/CharacterSkeleton]
[CharacterMesh]
parent = @Character
[MeshInstance3D]
mesh = "res://characters/knight.glb:mesh[0]"
skeleton = @CharacterSkeleton
[/MeshInstance3D]
[/CharacterMesh]
[RightHandSocket]
parent = @Character
[BoneAttachment3D]
skeleton = @CharacterSkeleton
bone = 15
[Node3D]
position = (0, 0, 0)
rotation = (0, 0, 0, 1)
scale = (1, 1, 1)
visible = true
[/Node3D]
[/BoneAttachment3D]
[/RightHandSocket]
[Sword]
parent = @RightHandSocket
[MeshInstance3D]
mesh = "res://weapons/sword.glb:mesh[0]"
material = "res://weapons/sword.pmat"
[Node3D]
position = (0.05, 0.0, 0.0)
rotation = (0, 0, 0, 1)
scale = (1, 1, 1)
visible = true
[/Node3D]
[/MeshInstance3D]
[/Sword]
Notes:
- Bone index comes from imported skeleton order.
- Meshes can share one skeleton when their
JOINTS_0/WEIGHTS_0indices are authored for that same skeleton order. - Shared-rig reuse needs no bake. Mismatched rigs use
.pretargetmaps to remap names and align rest poses before playback. bone = -1or missingskeletondisables attachment update.- If index is out of range, attachment keeps its current transform.
- Child nodes render/use physics from attachment transform like any other parented node.