Scene
Scene graph: a hierarchy of Nodes with Camera and Light components. Create a scene, add nodes/cameras/lights, then call draw() to render.
Scene.new()
Section titled “Scene.new()”Returns: Scene — A new empty scene.
Construct a new scene. Add nodes, cameras, and lights, then call draw each frame (typically after setting view/projection matrices on the graphics context).
_scene = Scene.new()var cam = _scene.addCamera()cam.setTag("main")var light = _scene.addLight()var node = _scene.addNode("Player")_scene.draw()addNode(name) / addCamera() / addLight()
Section titled “addNode(name) / addCamera() / addLight()”Returns: Node, Camera, or Light — The new object added to the scene.
Parameters:
name(String, required) — Name for the node. Can be an empty string.
Add a node, a camera, or a light. You can then set the camera/light tag and attach them to nodes via setNode(node).
name is required. Wren has no optional or default parameters — arity is part of the method signature, and Scene declares only addNode(_). Calling scene.addNode() does not create an unnamed node; it aborts the fiber with Scene does not implement 'addNode()'. For an unnamed node, pass an empty string:
var unnamed = _scene.addNode("")Where the engine genuinely does offer optional arguments, it declares each arity as a separate method — for example Graphics.newMesh ships both newMesh(vertices, drawMode) and newMesh(vertices, drawMode, shader).
Note one footgun: addNode does not type-check its argument. A non-String value (such as null) is silently treated as an empty name rather than raising an error, so a mistyped variable yields an unnamed node instead of a diagnostic.
getRoot()
Section titled “getRoot()”Returns: Node — The root node of the scene (all added nodes/cameras/lights are attached under this hierarchy).
attachNode(node)
Section titled “attachNode(node)”Parameters:
node(Node) — An existing node (e.g. from BlendResult.instantiate) to attach to the scene root.
Attach an existing node to the scene root so it is part of this scene.
draw()
Section titled “draw()”Draw the scene: render all geometry using the scene’s cameras and lights. Call after setting Graphics.setViewMatrix, Graphics.setProjectionMatrix, and Graphics.setViewProjectionEnabled(true) with the active camera’s matrices.
Graphics.setViewProjectionEnabled(true)var view = _camera.getViewMatrix()var proj = _camera.getProjectionMatrix(aspect)Graphics.setViewMatrix(view)Graphics.setProjectionMatrix(proj)_scene.draw()findCameraByTag(tag) / findLightByTag(tag)
Section titled “findCameraByTag(tag) / findLightByTag(tag)”Returns: Camera or Light, or null if not found.
Parameters:
tag(String) — Tag string set on the camera or light (e.g. viasetTag("main")).
Find a camera or light by its tag. Useful when loading a camera/light from a blend and you need to retrieve it by the tag configured in Blender.
var cam = _scene.findCameraByTag("main")if (cam != null) _camera = camfindNodeById(id)
Section titled “findNodeById(id)”Returns: Node or null — Find a node by its node_id string (set in Blender via the Plume3D addon or via node.node_id in Wren).
var door = _scene.findNodeById("door_01")Physics objects
Section titled “Physics objects”createCharacterController(height, radius, x, y, z)
Section titled “createCharacterController(height, radius, x, y, z)”Returns: CharacterController — A new capsule-based character controller placed at (x, y, z).
See CharacterController for the full API.
_controller = _scene.createCharacterController(1.8, 0.3, 0, 2, 0)_controller.node = _playerNodecreateParticleEmitter(name, maxBodies, size, mass, lifetime, emissionRate, x, y, z)
Section titled “createParticleEmitter(name, maxBodies, size, mass, lifetime, emissionRate, x, y, z)”Returns: ParticleEmitter — A new Jolt-backed particle emitter.
See ParticleEmitter for the full API.
_emitter = _scene.createParticleEmitter("sparks", 128, 0.05, 0.1, 2.0, 50, 0, 3, 0)_emitter.play()Instanced meshes
Section titled “Instanced meshes”createInstancedMesh(name)
Section titled “createInstancedMesh(name)”Returns: InstancedMesh — A new instanced mesh registered with this scene.
findInstancedMesh(name)
Section titled “findInstancedMesh(name)”Returns: InstancedMesh or null — Find an existing instanced mesh by name.
_trees = _scene.createInstancedMesh("trees")_trees.setMesh(_treeMesh)_trees.useGpuInstancing = truefor (i in 0...500) { _trees.addInstance(Math.sin(i) * 30, 0, Math.cos(i) * 30)}Loading a Blender scene
Section titled “Loading a Blender scene”Scene.loadBlendScene(path)
Section titled “Scene.loadBlendScene(path)”Returns: BlendResult or null — The loaded blend (so you can
query its nodes or instantiate scripted units), or null if the file could not be loaded.
Parameters:
path(String) — Path to the.blendfile relative to the mounted project (e.g."Models/FoliageTerrain.blend").
Load a Blender-authored .blend and instantiate its whole environment into this scene in one
call (engine PLM-253, Block BLD, ADR 0088) — terrain (render node +
automatic static collider), baked scatter (GPU-instanced foliage), any
plume3d_water-tagged water surfaces, and the static mesh
props (each prop also gaining a configurable static Jolt collider
if it carries a plume3d_collider tag, engine PLM-267). It is a convenience composition of the
instantiate primitives:
loadBlendScene(path) { var blend = Resource.loadBlend(path) if (blend == null) return null blend.instantiateTerrain(this) // ground + static Jolt collider (PLM-254) blend.instantiateWater(this) // plume3d_water-tagged meshes → auto-drawn water surfaces (PLM-274) blend.instantiateScatter(this) // baked foliage as GPU-instanced draws (PLM-252) blend.instantiateUnits(this) // static mesh props (+ per-object colliders, PLM-253/267) return blend}It returns the BlendResult so you can instantiate any class_name-scripted units
(characters, cameras, scripted props) explicitly — these are intentionally not
auto-instantiated, because they carry scripts/actions/drivers that must be bound to game logic:
var blend = _scene.loadBlendScene("Models/Level01.blend")if (blend != null) { var player = blend.instantiate("Player", _scene) // scripted unit — wired up by hand var cam = _scene.findCameraByTag("main")}Call the BlendResult primitives
(instantiateTerrain,
instantiateWater,
instantiateScatter,
instantiateUnits) directly when you want finer control
over order, selective categories, or per-unit handling.
See the Foliage + Terrain Demo example, which loads its entire
level (terrain + collider, 256 instanced grass blades, two rock props) with a single
loadBlendScene call.
See Node, Camera, and Light for those types. See Physics for addStaticBox, addDynamicBox, and body manipulation.