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>
131 lines
3.7 KiB
Text
131 lines
3.7 KiB
Text
# png.ludic - the renderer writes a frame as a PPM; the lab keeps it as a PNG, stored rather than
|
|
# compressed (a PNG anything opens, with no zlib here): the shot is the PPM's pixels, filter 0
|
|
# on every row, in deflate's stored blocks, with its CRCs and its Adler-32.
|
|
var lp_table: []long = null
|
|
|
|
function lp_u32(hi: int, lo: int) -> long { return long(hi) * long(65536) + long(lo) }
|
|
function lp_table_init() -> void {
|
|
if lp_table != null { return }
|
|
lp_table = new []long
|
|
let poly = lp_u32(60856, 33568) # 0xEDB88320
|
|
for n in 0 .. 256 {
|
|
var c = long(n)
|
|
for k in 0 .. 8 {
|
|
if (c & long(1)) != long(0) { c = (c >> long(1)) ^ poly } else { c = c >> long(1) }
|
|
}
|
|
push(lp_table, c)
|
|
}
|
|
}
|
|
# CRC-32 of out[from .. from + n)
|
|
function lp_crc(out: []byte, from: int, n: int) -> long {
|
|
lp_table_init()
|
|
let mask = lp_u32(65535, 65535)
|
|
var c = mask
|
|
for i in from .. from + n {
|
|
let k = int((c ^ long(out[i])) & long(255))
|
|
c = lp_table[k] ^ (c >> long(8))
|
|
}
|
|
return c ^ mask
|
|
}
|
|
function lp_put32(out: []byte, at: int, v: long) -> void {
|
|
out[at] = int((v >> long(24)) & long(255))
|
|
out[at + 1] = int((v >> long(16)) & long(255))
|
|
out[at + 2] = int((v >> long(8)) & long(255))
|
|
out[at + 3] = int(v & long(255))
|
|
}
|
|
function lp_tag(out: []byte, at: int, tag: string) -> void {
|
|
for i in 0 .. 4 { out[at + i] = tag[i] }
|
|
}
|
|
# a PPM's header: "P6", width, height, maxval, each after white space; returns the pixel offset
|
|
var lp_w: int = 0
|
|
var lp_h: int = 0
|
|
function lp_ppm_header(d: []byte) -> int {
|
|
var i = 2
|
|
var got = 0
|
|
var vals = new []int
|
|
while got < 3 and i < len(d) {
|
|
while i < len(d) and (d[i] == 32 or d[i] == 10 or d[i] == 13 or d[i] == 9) { i += 1 }
|
|
var v = 0
|
|
while i < len(d) and d[i] >= 48 and d[i] <= 57 {
|
|
v = v * 10 + (d[i] - 48)
|
|
i += 1
|
|
}
|
|
push(vals, v)
|
|
got += 1
|
|
}
|
|
lp_w = vals[0]
|
|
lp_h = vals[1]
|
|
return i + 1
|
|
}
|
|
# the PPM at `ppm` as a PNG at `png`; the PPM is removed
|
|
export function lab_ppm_to_png(ppm: string, png: string) -> bool {
|
|
let d = Fs.read_bytes(ppm)
|
|
if d == null or len(d) < 8 { return false }
|
|
let px = lp_ppm_header(d)
|
|
let w = lp_w
|
|
let h = lp_h
|
|
let row = w * 3 + 1
|
|
let raw = row * h
|
|
let blocks = (raw + 65534) / 65535
|
|
let idat = 2 + blocks * 5 + raw + 4
|
|
let total = 8 + 25 + 12 + idat + 12
|
|
let out = buffer(total)
|
|
let sig = [137, 80, 78, 71, 13, 10, 26, 10]
|
|
for i in 0 .. 8 { out[i] = sig[i] }
|
|
var at = 8
|
|
lp_put32(out, at, long(13))
|
|
lp_tag(out, at + 4, "IHDR")
|
|
lp_put32(out, at + 8, long(w))
|
|
lp_put32(out, at + 12, long(h))
|
|
out[at + 16] = 8
|
|
out[at + 17] = 2
|
|
out[at + 18] = 0
|
|
out[at + 19] = 0
|
|
out[at + 20] = 0
|
|
lp_put32(out, at + 21, lp_crc(out, at + 4, 17))
|
|
at += 25
|
|
lp_put32(out, at, long(idat))
|
|
lp_tag(out, at + 4, "IDAT")
|
|
var o = at + 8
|
|
out[o] = 120
|
|
out[o + 1] = 1
|
|
o += 2
|
|
var a = 1
|
|
var b = 0
|
|
var left = raw
|
|
var k = 0 # the byte of the raw stream being written
|
|
while left > 0 {
|
|
let n = Math.min(left, 65535)
|
|
left -= n
|
|
var fin = 0
|
|
if left == 0 { fin = 1 }
|
|
out[o] = fin
|
|
out[o + 1] = n & 255
|
|
out[o + 2] = (n >> 8) & 255
|
|
out[o + 3] = (~n) & 255
|
|
out[o + 4] = ((~n) >> 8) & 255
|
|
o += 5
|
|
for j in 0 .. n {
|
|
let y = k / row
|
|
let x = k % row
|
|
var v = 0
|
|
if x > 0 { v = d[px + y * w * 3 + x - 1] }
|
|
out[o] = v
|
|
a = (a + v) % 65521
|
|
b = (b + a) % 65521
|
|
o += 1
|
|
k += 1
|
|
}
|
|
}
|
|
lp_put32(out, o, long(b) * long(65536) + long(a))
|
|
o += 4
|
|
lp_put32(out, o, lp_crc(out, at + 4, o - at - 4))
|
|
o += 4
|
|
lp_put32(out, o, long(0))
|
|
lp_tag(out, o + 4, "IEND")
|
|
lp_put32(out, o + 8, lp_crc(out, o + 4, 4))
|
|
o += 12
|
|
if not Fs.write_bytes(png, out, o) { return false }
|
|
Fs.remove(ppm)
|
|
return true
|
|
}
|