packages: ludic.ui's render3d backend threads Render3dState; render3d's settable vars (r3d_dem_path, post_*, grass_*, wt_wade_*, ...) are Render3dState's fields again, not lets

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 17:11:15 +03:00
parent ce80e64b24
commit 3a87d02696
15 changed files with 152 additions and 148 deletions

View file

@ -43,38 +43,38 @@ function r3u_scale(ui_st: UiState) -> float { return float(gl_height()) / 1080.0
function r3u_r(c: int) -> float { return float((c / 65536) % 256) / 255.0 }
function r3u_g(c: int) -> float { return float((c / 256) % 256) / 255.0 }
function r3u_b(c: int) -> float { return float(c % 256) / 255.0 }
function r3u_rect(x: float, y: float, w: float, h: float, c: int, a: float) -> void { ov_rect(int(x), int(y), int(w), int(h), r3u_r(c), r3u_g(c), r3u_b(c), a) }
function r3u_rect(render3d_st: mut Render3dState, x: float, y: float, w: float, h: float, c: int, a: float) -> void { ov_rect(render3d_st, int(x), int(y), int(w), int(h), r3u_r(c), r3u_g(c), r3u_b(c), a) }
# a rounded rectangle: a cross of two rects and a disc in each corner
function r3u_round(x: float, y: float, w: float, h: float, rad0: float, c: int, a: float) -> void {
function r3u_round(render3d_st: mut Render3dState, x: float, y: float, w: float, h: float, rad0: float, c: int, a: float) -> void {
let rad = Math.min(rad0, Math.min(w, h) / 2.0)
let r = r3u_r(c)
let g = r3u_g(c)
let b = r3u_b(c)
ov_rect(int(x + rad), int(y), int(w - rad * 2.0), int(h), r, g, b, a)
ov_rect(int(x), int(y + rad), int(rad), int(h - rad * 2.0), r, g, b, a)
ov_rect(int(x + w - rad), int(y + rad), int(rad), int(h - rad * 2.0), r, g, b, a)
ov_disc(x + rad, y + rad, rad, 12, r, g, b, a)
ov_disc(x + w - rad, y + rad, rad, 12, r, g, b, a)
ov_disc(x + rad, y + h - rad, rad, 12, r, g, b, a)
ov_disc(x + w - rad, y + h - rad, rad, 12, r, g, b, a)
ov_rect(render3d_st, int(x + rad), int(y), int(w - rad * 2.0), int(h), r, g, b, a)
ov_rect(render3d_st, int(x), int(y + rad), int(rad), int(h - rad * 2.0), r, g, b, a)
ov_rect(render3d_st, int(x + w - rad), int(y + rad), int(rad), int(h - rad * 2.0), r, g, b, a)
ov_disc(render3d_st, x + rad, y + rad, rad, 12, r, g, b, a)
ov_disc(render3d_st, x + w - rad, y + rad, rad, 12, r, g, b, a)
ov_disc(render3d_st, x + rad, y + h - rad, rad, 12, r, g, b, a)
ov_disc(render3d_st, x + w - rad, y + h - rad, rad, 12, r, g, b, a)
}
# a rounded frame: four straight edges and a quarter arc at each corner
function r3u_ring(x: float, y: float, w: float, h: float, rad0: float, t: float, c: int, a: float) -> void {
function r3u_ring(render3d_st: mut Render3dState, x: float, y: float, w: float, h: float, rad0: float, t: float, c: int, a: float) -> void {
let rad = Math.min(rad0, Math.min(w, h) / 2.0)
let r = r3u_r(c)
let g = r3u_g(c)
let b = r3u_b(c)
ov_rect(int(x + rad), int(y), int(w - rad * 2.0), int(t), r, g, b, a)
ov_rect(int(x + rad), int(y + h - t), int(w - rad * 2.0), int(t), r, g, b, a)
ov_rect(int(x), int(y + rad), int(t), int(h - rad * 2.0), r, g, b, a)
ov_rect(int(x + w - t), int(y + rad), int(t), int(h - rad * 2.0), r, g, b, a)
ov_rect(render3d_st, int(x + rad), int(y), int(w - rad * 2.0), int(t), r, g, b, a)
ov_rect(render3d_st, int(x + rad), int(y + h - t), int(w - rad * 2.0), int(t), r, g, b, a)
ov_rect(render3d_st, int(x), int(y + rad), int(t), int(h - rad * 2.0), r, g, b, a)
ov_rect(render3d_st, int(x + w - t), int(y + rad), int(t), int(h - rad * 2.0), r, g, b, a)
let q = 1.5707964
ov_arc(x + rad, y + rad, rad - t / 2.0, t, q * 2.0, q, 8, r, g, b, a)
ov_arc(x + w - rad, y + rad, rad - t / 2.0, t, q * 3.0, q, 8, r, g, b, a)
ov_arc(x + w - rad, y + h - rad, rad - t / 2.0, t, 0.0, q, 8, r, g, b, a)
ov_arc(x + rad, y + h - rad, rad - t / 2.0, t, q, q, 8, r, g, b, a)
ov_arc(render3d_st, x + rad, y + rad, rad - t / 2.0, t, q * 2.0, q, 8, r, g, b, a)
ov_arc(render3d_st, x + w - rad, y + rad, rad - t / 2.0, t, q * 3.0, q, 8, r, g, b, a)
ov_arc(render3d_st, x + w - rad, y + h - rad, rad - t / 2.0, t, 0.0, q, 8, r, g, b, a)
ov_arc(render3d_st, x + rad, y + h - rad, rad - t / 2.0, t, q, q, 8, r, g, b, a)
}
function r3u_text(x: float, y: float, s: string, size: float, c: int, a: float) -> void { ov_text(int(x), int(y), int(size), s, r3u_r(c), r3u_g(c), r3u_b(c), a) }
function r3u_measure(s: string, size: float) -> float { return float(ov_text_w(int(size), s)) }
function r3u_clip(x: float, y: float, w: float, h: float) -> void { ov_clip(int(x), int(y), int(w), int(h)) }
function r3u_unclip() -> void { ov_unclip() }
function r3u_text(render3d_st: mut Render3dState, x: float, y: float, s: string, size: float, c: int, a: float) -> void { ov_text(render3d_st, int(x), int(y), int(size), s, r3u_r(c), r3u_g(c), r3u_b(c), a) }
function r3u_measure(render3d_st: mut Render3dState, s: string, size: float) -> float { return float(ov_text_w(render3d_st, int(size), s)) }
function r3u_clip(render3d_st: mut Render3dState, x: float, y: float, w: float, h: float) -> void { ov_clip(render3d_st, int(x), int(y), int(w), int(h)) }
function r3u_unclip(render3d_st: mut Render3dState) -> void { ov_unclip(render3d_st) }

View file

@ -1,37 +1,37 @@
# render3d_image.ludic - pictures for the render3d backend: a texture by path (its sRGB bytes as they
# are, clamped at its edges, loaded again when the file changes) and a cell of a named atlas
module ludic_ui
function r3u_tex(ui_st: mut UiState, path: string) -> int {
function r3u_tex(render3d_st: mut Render3dState, ui_st: mut UiState, path: string) -> int {
for i in 0 .. len(ui_st.r3u_tex_paths) {
if ui_st.r3u_tex_paths[i] == path { return r3u_tex_fresh(ui_st, i) }
if ui_st.r3u_tex_paths[i] == path { return r3u_tex_fresh(render3d_st, ui_st, i) }
}
push(ui_st.r3u_tex_paths, path)
push(ui_st.r3u_tex_ids, r3u_tex_load(path))
push(ui_st.r3u_tex_ids, r3u_tex_load(render3d_st, path))
push(ui_st.r3u_tex_sizes, Fs.size(path))
push(ui_st.r3u_tex_seen, r3u_now())
return ui_st.r3u_tex_ids[len(ui_st.r3u_tex_ids) - 1]
}
# a picture is looked at again every half second: a file that has changed (or that was not there,
# and is now) is loaded again, so an <img> of a photograph written over the old one shows the new one
function r3u_tex_fresh(ui_st: mut UiState, i: int) -> int {
function r3u_tex_fresh(render3d_st: mut Render3dState, ui_st: mut UiState, i: int) -> int {
let now = r3u_now()
if now - ui_st.r3u_tex_seen[i] < 0.5 and now >= ui_st.r3u_tex_seen[i] { return ui_st.r3u_tex_ids[i] }
ui_st.r3u_tex_seen[i] = now
let size = Fs.size(ui_st.r3u_tex_paths[i])
if size == ui_st.r3u_tex_sizes[i] and (ui_st.r3u_tex_ids[i] != 0 or size < 0) { return ui_st.r3u_tex_ids[i] }
if ui_st.r3u_tex_ids[i] != 0 { gpu_tex_free(ui_st.r3u_tex_ids[i]) }
ui_st.r3u_tex_ids[i] = r3u_tex_load(ui_st.r3u_tex_paths[i])
if ui_st.r3u_tex_ids[i] != 0 { gpu_tex_free(render3d_st, ui_st.r3u_tex_ids[i]) }
ui_st.r3u_tex_ids[i] = r3u_tex_load(render3d_st, ui_st.r3u_tex_paths[i])
ui_st.r3u_tex_sizes[i] = size
return ui_st.r3u_tex_ids[i]
}
# the interface is drawn in sRGB, and a picture's bytes are sRGB, so they are taken as they are
# (not decoded to linear, which would darken it); clamped at its edges
function r3u_tex_load(path: string) -> int {
let t = tex_load(path, false)
function r3u_tex_load(render3d_st: mut Render3dState, path: string) -> int {
let t = tex_load(render3d_st, path, false)
if t != 0 {
gpu_tex_bind(GPU_TEX2D, t)
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
gpu_tex_param(GPU_TEX2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
gpu_tex_bind(render3d_st, GPU_TEX2D, t)
gpu_tex_param(render3d_st, GPU_TEX2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
gpu_tex_param(render3d_st, GPU_TEX2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
}
return t
}
@ -44,7 +44,7 @@ export function ui_image_reload(ui_st: mut UiState, path: string) -> void {
}
}
}
function r3u_image(ui_st: mut UiState, src: string, x: float, y: float, w: float, h: float, c: int, a: float) -> void {
function r3u_image(render3d_st: mut Render3dState, ui_st: mut UiState, src: string, x: float, y: float, w: float, h: float, c: int, a: float) -> void {
var colon = -1
for i in 0 .. len(src) {
if colon < 0 and src[i] == 58 { colon = i }
@ -53,22 +53,22 @@ function r3u_image(ui_st: mut UiState, src: string, x: float, y: float, w: float
let prefix: string = src[0..colon]
for k in 0 .. len(ui_st.r3u_atlas) {
if ui_st.r3u_atlas[k] == prefix {
r3u_cell(ui_st, k, src[colon + 1..len(src)], x, y, w, h, c, a)
r3u_cell(render3d_st, ui_st, k, src[colon + 1..len(src)], x, y, w, h, c, a)
return
}
}
}
let t = r3u_tex(ui_st, src)
if t != 0 { ov_sub(t, int(x), int(y), int(w), int(h), 0.0, 0.0, 1.0, 1.0, r3u_r(c), r3u_g(c), r3u_b(c), a) }
let t = r3u_tex(render3d_st, ui_st, src)
if t != 0 { ov_sub(render3d_st, t, int(x), int(y), int(w), int(h), 0.0, 0.0, 1.0, 1.0, r3u_r(c), r3u_g(c), r3u_b(c), a) }
}
# a picture's own size, for object-fit; an atlas cell is drawn square
function r3u_image_w(ui_st: mut UiState, src: string) -> float {
function r3u_image_w(render3d_st: mut Render3dState, ui_st: mut UiState, src: string) -> float {
if r3u_is_cell(ui_st, src) { return 1.0 }
return float(tex_width(r3u_tex(ui_st, src)))
return float(tex_width(render3d_st, r3u_tex(render3d_st, ui_st, src)))
}
function r3u_image_h(ui_st: mut UiState, src: string) -> float {
function r3u_image_h(render3d_st: mut Render3dState, ui_st: mut UiState, src: string) -> float {
if r3u_is_cell(ui_st, src) { return 1.0 }
return float(tex_height(r3u_tex(ui_st, src)))
return float(tex_height(render3d_st, r3u_tex(render3d_st, ui_st, src)))
}
function r3u_is_cell(ui_st: UiState, src: string) -> bool {
for k in 0 .. len(ui_st.r3u_atlas) {
@ -77,7 +77,7 @@ function r3u_is_cell(ui_st: UiState, src: string) -> bool {
}
return false
}
function r3u_cell(ui_st: mut UiState, k: int, name: string, x: float, y: float, w: float, h: float, c: int, a: float) -> void {
function r3u_cell(render3d_st: mut Render3dState, ui_st: mut UiState, k: int, name: string, x: float, y: float, w: float, h: float, c: int, a: float) -> void {
let names = ui_st.r3u_atlas_names[k]
var idx = -1
for i in 0 .. len(names) {
@ -95,5 +95,5 @@ function r3u_cell(ui_st: mut UiState, k: int, name: string, x: float, y: float,
let u0 = float(cx) / float(cols)
let v0 = float(cy) / float(rows)
let s = Math.min(w, h)
ov_sub(ui_st.r3u_atlas_tex[k], int(x + (w - s) / 2.0), int(y + (h - s) / 2.0), int(s), int(s), u0, v0, u0 + 1.0 / float(cols), v0 + 1.0 / float(rows), r3u_r(c), r3u_g(c), r3u_b(c), a)
ov_sub(render3d_st, ui_st.r3u_atlas_tex[k], int(x + (w - s) / 2.0), int(y + (h - s) / 2.0), int(s), int(s), u0, v0, u0 + 1.0 / float(cols), v0 + 1.0 / float(rows), r3u_r(c), r3u_g(c), r3u_b(c), a)
}

View file

@ -3,11 +3,11 @@ module ludic_ui
# border-image: the texture's `slice`-pixel corners kept, the rest stretched, drawn `dst` wide (at
# most half the box each way, ui_nine_cuts); the slice is a share of the texture's own width across
# and its own height down, so it need not be square
function r3u_nine(ui_st: mut UiState, src: string, slice: float, x: float, y: float, w: float, h: float, dst: float, c: int, a: float) -> void {
let t = r3u_tex(ui_st, src)
function r3u_nine(render3d_st: mut Render3dState, ui_st: mut UiState, src: string, slice: float, x: float, y: float, w: float, h: float, dst: float, c: int, a: float) -> void {
let t = r3u_tex(render3d_st, ui_st, src)
if t == 0 { return }
let su = slice / float(Math.max(tex_width(t), 1))
let sv = slice / float(Math.max(tex_height(t), 1))
let su = slice / float(Math.max(tex_width(render3d_st, t), 1))
let sv = slice / float(Math.max(tex_height(render3d_st, t), 1))
let cut = ui_nine_cuts(x, y, w, h, dst)
let xs = r3u_four(cut[0], cut[1], cut[2], cut[3])
let ys = r3u_four(cut[4], cut[5], cut[6], cut[7])
@ -17,7 +17,7 @@ function r3u_nine(ui_st: mut UiState, src: string, slice: float, x: float, y: fl
for i in 0 .. 3 {
let x0 = int(xs[i])
let y0 = int(ys[j])
ov_sub(t, x0, y0, int(xs[i + 1]) - x0, int(ys[j + 1]) - y0, us[i], vs[j], us[i + 1], vs[j + 1], r3u_r(c), r3u_g(c), r3u_b(c), a)
ov_sub(render3d_st, t, x0, y0, int(xs[i + 1]) - x0, int(ys[j + 1]) - y0, us[i], vs[j], us[i + 1], vs[j + 1], r3u_r(c), r3u_g(c), r3u_b(c), a)
}
}
}