Render Layers¶
choose which objects a camera draws.
▶ Run in browserTags: 3d
Every 3D node has a render_layer bitmask saying which of the 32 render
layers it belongs to, and every camera has a cull_mask saying which layers
it draws. A node is drawn when the two share a bit. A node starts on layer 0
(render_layer=1) and a camera draws every layer, so nothing is hidden until
you say so:
MeshInstance3D(mesh=Mesh.box(), render_layer=1 << 2) # layer 2 only
camera.cull_mask ^= 1 << 2 # show or hide layer 2
Both are plain integers, so a node can sit on several layers at once: the
ground here is on layers 0, 1 and 2 (0b111) and stays visible whatever the
camera hides. Layers hide objects from one camera without removing them from
the scene, which is how a minimap, a first-person view without the player’s own
body, or an editor-only gizmo is built.
The red cubes are on layer 0, the green spheres on layer 1 and the blue cubes on
layer 2. Each button is Button(action=name), so it presses the same action
as its number key and one @on_input handler serves both; the handler flips
the layer’s bit in the camera’s cull_mask and shows the result on the
button. The camera is an OrbitCamera3D, which orbits on a drag by itself;
on_update adds keyboard orbit.
Controls: 1 / 2 / 3 / buttons - Show or hide the red, green or blue layer A / D, Left / Right - Orbit the camera Drag - Orbit the camera
Source¶
1"""Render Layers: choose which objects a camera draws.
2
3Every 3D node has a ``render_layer`` bitmask saying which of the 32 render
4layers it belongs to, and every camera has a ``cull_mask`` saying which layers
5it draws. A node is drawn when the two share a bit. A node starts on layer 0
6(``render_layer=1``) and a camera draws every layer, so nothing is hidden until
7you say so:
8
9 MeshInstance3D(mesh=Mesh.box(), render_layer=1 << 2) # layer 2 only
10 camera.cull_mask ^= 1 << 2 # show or hide layer 2
11
12Both are plain integers, so a node can sit on several layers at once: the
13ground here is on layers 0, 1 and 2 (``0b111``) and stays visible whatever the
14camera hides. Layers hide objects from one camera without removing them from
15the scene, which is how a minimap, a first-person view without the player's own
16body, or an editor-only gizmo is built.
17
18The red cubes are on layer 0, the green spheres on layer 1 and the blue cubes on
19layer 2. Each button is ``Button(action=name)``, so it presses the same action
20as its number key and one ``@on_input`` handler serves both; the handler flips
21the layer's bit in the camera's ``cull_mask`` and shows the result on the
22button. The camera is an ``OrbitCamera3D``, which orbits on a drag by itself;
23``on_update`` adds keyboard orbit.
24
25Controls:
26 1 / 2 / 3 / buttons - Show or hide the red, green or blue layer
27 A / D, Left / Right - Orbit the camera
28 Drag - Orbit the camera
29"""
30
31import math
32
33from simvx.core import (
34 Anchor,
35 Axis1D,
36 Button,
37 CanvasLayer,
38 DirectionalLight3D,
39 HBoxContainer,
40 Input,
41 Key,
42 Label,
43 Material,
44 Mesh,
45 MeshInstance3D,
46 Node3D,
47 OrbitCamera3D,
48 on_input,
49)
50from simvx.graphics import App
51
52# Action name -> (render layer, button label, colour, mesh).
53LAYERS = {
54 "red": (0, "Red cubes", (0.9, 0.15, 0.1), Mesh.box(0.8)),
55 "green": (1, "Green spheres", (0.1, 0.85, 0.2), Mesh.sphere(0.6)),
56 "blue": (2, "Blue cubes", (0.1, 0.2, 0.9), Mesh.box(0.8)),
57}
58
59
60class RenderLayers(Node3D):
61 input_context = {
62 "red": [Key.KEY_1],
63 "green": [Key.KEY_2],
64 "blue": [Key.KEY_3],
65 "orbit": [Axis1D(low=Key.A, high=Key.D), Axis1D(low=Key.LEFT, high=Key.RIGHT)],
66 }
67
68 def on_ready(self):
69 self.camera = self.add_child(
70 OrbitCamera3D(pivot=(0, 0.5, 0), distance=14, yaw=math.radians(30), pitch=math.radians(-32), fov=60)
71 )
72 self.add_child(DirectionalLight3D(colour=(1.0, 0.95, 0.9), intensity=1.2, direction=(-0.4, -1.0, -0.6)))
73 ground = Material(colour=(0.3, 0.3, 0.3))
74 self.add_child(
75 MeshInstance3D(mesh=Mesh.box((20, 0.1, 20)), material=ground, position=(0, -0.05, 0), render_layer=0b111)
76 )
77
78 hud = self.add_child(CanvasLayer())
79 bar = hud.add_child(HBoxContainer(anchor=Anchor.TOP_LEFT, inset=12))
80 self.buttons = {}
81 for row, (name, (layer, label, colour, mesh)) in enumerate(LAYERS.items()):
82 material = Material(colour=colour)
83 for i in range(3):
84 position = (-4 + i * 2, 1.0, -2 + row * 2)
85 self.add_child(MeshInstance3D(mesh=mesh, material=material, position=position, render_layer=1 << layer))
86 self.buttons[name] = bar.add_child(Button(label, action=name, checked=True))
87
88 # Example help overlay: not part of the technique shown.
89 hud.add_child(Label("A/D or drag: orbit", anchor=Anchor.BOTTOM_LEFT, inset=12, font_size=14))
90
91 @on_input(("red", "green", "blue"))
92 def toggle_layer(self, event):
93 bit = 1 << LAYERS[event.action][0]
94 self.camera.cull_mask ^= bit
95 self.buttons[event.action].checked = bool(self.camera.cull_mask & bit)
96
97 def on_update(self, dt):
98 self.camera.orbit(Input.value("orbit") * math.radians(60) * dt, 0)
99
100
101if __name__ == "__main__":
102 App(title="Render Layers", width=1280, height=720).run(RenderLayers())