The scene's distance-fog sub-handle, or nil when fog is off. Assign a table of {color, near, far} to enable/configure it, or nil to disable.
Example
-- Clear up to 5 units from the camera, solid from 20 on.
scene.fog = { color = color("#2d353b"), near = 5, far = 20 }
scene:plane({ scale = vec3(40, 1, 40), color = color("#a7c080") })
The scene's vignette sub-handle, or nil when off. Assign a table of {intensity, radius, smoothness, roundness, edge_compensation, center, color} to enable/configure it, or nil to disable.
Example
-- Keys left out keep their defaults; center is in 0..1 image space.
scene.vignette =
{ intensity = 0.6, radius = 0.8, color = color("#1e2326") }
print(scene.vignette.center) --> vec2(0.5, 0.5)
The scene's lens-distortion sub-handle, or nil when off. Assign a table of {intensity, scale, multiplier, center, edge_curvature} to enable/configure it, or nil to disable.
Example
-- Positive intensity bulges the image; scale crops the edges.
scene.lens_distortion = { intensity = 0.3, scale = 1.1 }
The scene's chromatic-aberration sub-handle, or nil when off. Assign a table of {intensity, max_samples, color_lut} to enable/configure it, or nil to disable.
Example
-- intensity is the streak size as a fraction of the window.
scene.chromatic_aberration = { intensity = 0.02 }
The scene's reference grid, or nil when off. Assign a table of {x_axis_color, z_axis_color, minor_line_color, major_line_color, fadeout_distance, dot_fadeout_strength, scale} to enable/configure it, or nil to disable.
Example
-- A reference grid on the XZ plane; every 10th line is a major line.
scene.grid = { scale = 1, fadeout_distance = 40 }
-- A 1 x 1 square in the XZ plane, facing +Y; here a 10 x 10 floor.local floor = scene:plane({
scale = vec3(10, 1, 10),
subdivisions = 16,
color = color("#a7c080"),
})
print(floor:bounds().size) --> vec3(1.0, 0.0, 1.0)
-- Upright along Y, 1 unit long and 1 across; here a pipe lying along X.local pipe = scene:cylinder({
rotation = datumhue.math.quat.from_rotation_z(math.pi / 2),
scale = vec3(0.5, 3, 0.5),
color = color("#7fbbb3"),
})
print(pipe:bounds().size) --> vec3(1.0, 1.0, 1.0)
-- 2 units tall: a 1 unit body between two half-unit caps.local pill =
scene:capsule({ pos = vec3(0, 1, 0), color = color("#d699b6") })
print(pill:bounds().size) --> vec3(1.0, 2.0, 1.0)
-- A ring 1 unit across lying in the XZ plane.local ring =
scene:torus({ pos = vec3(0, 0.5, 0), color = color("#dbbc7f") })
print(ring:bounds().size) --> vec3(1.0, 0.19999998807907104, 1.0)
-- 1 unit tall around pos, base 1 unit across, the tip toward +Y.local spike =
scene:cone({ pos = vec3(0, 0.5, 0), color = color("#e69875") })
print(spike:bounds().size) --> vec3(1.0, 1.0, 1.0)
Set image-based environment lighting from prefiltered cubemaps, or nil to remove it. Requires diffuse and specular cubemaps; raises if either is missing.
Example
-- Both maps are cubemaps; intensity is in candela per square meter.local faces = datumhue.image.new({ width = 16, height = 96 })
faces:apply(datumhue.image.picture():fill(color("#d3c6aa")))
local map = faces:to_cubemap()
scene:set_environment({
diffuse = map,
specular = map,
intensity = 800,
})
Remove every node in the scene — meshes, models, lights, voxels, and groups (a parented child goes with its parent). The camera and its settings (background, ambient, fog, bloom, tonemapping, exposure) are kept.
Scene:remove
method
Scene:remove()
Remove the scene wholesale — every node, the camera and its settings, and any UI element mounting it — freeing its render target. Afterwards every other method and property of the handle raises, and calling remove again does nothing. Other handles still holding the texture (a shader channel, a material) keep the last rendered frame.
Group existing nodes under a new empty frame node and return it; moving or reparenting the frame moves the whole group (a shared transform frame).
Example
-- The new node sits at the origin; moving it moves both cubes.local left = scene:cube({ pos = vec3(-1, 0.5, 0) })
local right = scene:cube({ pos = vec3(1, 0.5, 0) })
local pair = scene:group({ left, right })
pair.pos = vec3(0, 0, -2)
-- Each cube keeps its position, now relative to the group.print(left.pos) --> vec3(-1.0, 0.5, 0.0)
Spawn a billboarded sprite: a textured quad turned to face the scene camera every frame. Pass either source (a Sprite from image:sprite/atlas:sprite) or image (a whole Image). The returned SceneNode carries the sprite-only properties billboard, size_mode, size, pivot, alpha, cutoff, color, flip_x, flip_y, shaded, and cell, alongside the usual node pos/visible/parent. While billboard is not none, that turn sets the node's rotation every frame, replacing any rotation assigned to it. Animate the atlas cell by binding a sprite animator, or by setting cell each frame.
Example
-- A 1 x 2 unit quad that turns to face the camera, its feet at pos.local art = datumhue.image.new({ width = 16, height = 32 })
art:apply(datumhue.image.picture():fill(color("#a7c080")))
scene:sprite({
image = art,
pos = vec3(0, 0, 0),
size = datumhue.math.vec2(1, 2),
})
Scene:camera_relative_yaw(node: SceneNode) -> number
The horizontal angle (radians, -pi..pi) from node to the scene camera, in world space. Combine with the character's own facing to choose a directional sprite row.
Example
-- The camera starts at (0, 5, 10); this cube sits 10 units to its -X.local guard = scene:cube({ pos = vec3(-10, 0.5, 10) })
-- Positions take effect at the end of the frame.datumhue.time.wait_frames(1)
-- Radians about +Y, 0 when the camera is straight ahead in +Z.print(scene:camera_relative_yaw(guard)) --> 1.5707963705062866
The horizontal camera angle from node quantized to one of n evenly-spaced buckets (0..n-1, bucket 0 centered on the camera being straight ahead in +Z). Map the bucket to a directional sprite row, e.g. an 8-way character.
Example
-- The camera starts at (0, 5, 10); this cube sits 10 units to its -X.local guard = scene:cube({ pos = vec3(-10, 0.5, 10) })
datumhue.time.wait_frames(1)
-- 0-based: bucket 0 is the camera straight ahead in +Z, 2 is +X.print(scene:camera_yaw_bucket(guard, 8)) --> 2
Cast a ray into the scene. Returns the nearest of this scene's nodes it hits, or nil if the ray missed them all.
Example
local target = scene:cube({ pos = vec3(0, 0.5, 0) })
-- A new node can be hit once its frame has ended.datumhue.time.wait_frames(1)
local ray = datumhue.math.ray(vec3(0, 0.5, 5), vec3(0, 0, -1))
local hit = scene:raycast(ray)
print(hit.node == target, hit.distance) --> true 4.5print(hit.point, hit.normal) --> vec3(0.0, 0.5, 0.5) vec3(0.0, 0.0, 1.0)
Convert viewport pixel coordinates to a world-space ray, or nil if off-screen. x/y are pixels in the scene's rendered image — origin top-left, Y-down, spanning the scene's own width and height at any resolution it renders at. No display-fit adjustment is applied: when the mounted element scales or letterboxes the scene, convert the cursor into image pixels first.
Example
-- Pixels of the scene's own 320 x 240 image: origin top left, Y down.-- The camera can map pixels once its first frame has ended.datumhue.time.wait_frames(1)
local ray = scene:screen_to_ray(160, 120)
-- The center pixel looks from the camera at (0, 5, 10) to the origin.print(ray.direction) --> vec3(0.0, -0.44721364974975586, -0.89442718029022217)
How the texture maps into the element box; aspect-preserving modes center in the box. "sharp" draws with its own shader, so it takes no material; neither "integer" nor "sharp" takes auto_size. "integer" scales a draw canvas's or scene's size in UI px, so on one whose texels are finer than that (a canvas without pixel_perfect on a HiDPI display, a scene) a texel covers a fraction of the multiple. Pair it with the source's filter set to "nearest".
auto_size
boolean
false
For a draw canvas or scene: resize it to the element's size in UI px whenever the element resizes, so what it draws keeps its size in UI px instead of scaling with the element. A scene renders at its own size, so this also keeps one sharp. Raises on a plain image, whose resolution is fixed (use fit).
accessible_label
string
the image has no accessible name
Accessible name for assistive technology. Images carry no text, so unlike text-bearing widgets they derive no name of their own. Set it on images that convey meaning, and decorative on the rest.
decorative
boolean
false
Keeps the image out of what assistive technology reads, as alt="" does on the web: a screen reader passes over it. Set it on an image that only decorates, such as art beside text that says who or what it shows. Setting both this and accessible_label raises, and so does naming the image later.