ludic/packages/ludic.lab/loop.ludic
Orkuncakilkaya b401f7acc1 feat(ludic.lab): the visual lab as a package - a scene on a plate, never a world
A scene is an entry in the open registry LabScenes, shown on a plate: a
flat lit disc, the package's own small sky with the sun at one hour, and
a frame clock. Headless each camera (lab_shot, lab_shot_at) settles and
is written as build/lab/<scene>/<shot>.png (a stored PNG, written here);
in a window N and P step through them. tools/lab/run.sh and sheet.py
make the contact sheet; tools/lab/plate_build.py makes plate/.
ludic.render3d gains r3d_plate_mode (no terrain, grass or water is made)
and r3d_sky_path. quit() in a program with no frame loop links. The
example takes ~120 MB resident, 417 MiB peak footprint. Reseed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:48:43 +03:00

50 lines
1.6 KiB
Text

# loop.ludic - each frame: the scene's tick by a fixed step, the shot's camera, its caption, and -
# headless - the picture once the shot has settled, then the next shot, then quit. In a window
# N and P step through the shots.
export function lab_input() -> void {
if not lb_windowed { return }
Input.poll()
let n = lab_shot_count()
if n > 0 and Input.key_pressed(key: 'n') {
lb_shot = (lb_shot + 1) % n
lab_shot_apply(lb_shot)
}
if n > 0 and Input.key_pressed(key: 'p') {
lb_shot = (lb_shot + n - 1) % n
lab_shot_apply(lb_shot)
}
}
export function lab_render() -> void {
lb_frame += 1
let k = labscenes_find(lb_scene)
if k >= 0 and LabScenes[k].tick != null { LabScenes[k].tick(1.0 / 60.0) }
r3d_frame(lab_time())
lab_caption()
if not lb_windowed { lab_take() }
r3d_present()
}
function lab_caption() -> void {
if lb_font == "" { return }
ov_begin()
var name = ""
if lb_shot < lab_shot_count() { name = lb_shot_names[lb_shot] }
ov_rect(0, 0, 900, 64, 0.0, 0.0, 0.0, 0.55)
ov_text(20, 10, 30, `{lb_scene} / {name} ({lb_shot + 1} of {lab_shot_count()})`, 1.0, 1.0, 1.0, 1.0)
if len(lb_note) > 0 { ov_text(20, 42, 18, lb_note, 0.8, 0.8, 0.8, 1.0) }
ov_end()
}
function lab_take() -> void {
lb_wait += 1
if lb_wait < lb_settle { return }
let base = `{lb_out}/{lb_shot_names[lb_shot]}`
r3d_screenshot(`{base}.ppm`)
if lab_ppm_to_png(`{base}.ppm`, `{base}.png`) { print(`lab: shot {base}.png`) } else { print(`lab: shot {base}.ppm`) }
lb_shots_taken += 1
lb_wait = 0
lb_shot += 1
if lb_shot >= lab_shot_count() {
quit()
return
}
lab_shot_apply(lb_shot)
}