灵符lingfu

lingmodeler

↓ 0 downloads

LingModeler: box modeling with quads and tris, rigging + weight painting, 2D vector illustration and animation, procedural textures and game assets; .lmesh / .ling native formats, OBJ, glTF and SVG import / export

Windows · x86-64Linux · x86-64LingOS · x86-64LingOS · Pi 5 ARM64Web · WASM
Keywords 3d, modeling, vector, svg, gltf, obj, rigging, animation, textures, ling
lingfu install lingmodeler
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (67)

ling/plat_os.ling
# ━━━ plat_os.ling — LingOS platform layer (in-kernel ling runtime: lingrt / lingapp) ━━━
# The kernel has px_* drawing, fonts, files and dialogs but no polygon fill, no images and
# no byte files. Polygons are scan-converted here with px_hline; image features report "unavailable".

# no list_join in the kernel runtime: join pairwise (halves the list each pass)
fn p_join(l, sep) {
    if len(l) < 0.5 { return "" }
    bind cur = list_new()
    bind i = 0.0
    while i < len(l) { bind cur = list_push(cur, "" + list_get(l, i)) bind i = i + 1.0 }
    while len(cur) > 1.5 {
        bind nxt = list_new()
        bind i = 0.0
        while i < len(cur) {
            if i + 1.0 < len(cur) { bind nxt = list_push(nxt, list_get(cur, i) + sep + list_get(cur, i + 1.0)) }
            else { bind nxt = list_push(nxt, list_get(cur, i)) }
            bind i = i + 2.0
        }
        bind cur = nxt
    }
    return list_get(cur, 0)
}
fn p_frame_hook(frame) { if frame % 50.0 == 0.0 { println("lingmodeler frame ", frame, " mem ", os_info("memory")) } }
# The kernel runtime frees nothing yet, so an unchanged screen is not rebuilt. Returns true when nothing happened
# since the last drawn frame (no pointer move, button, wheel, key or animation); the caller then only presents.
fn p_idle_frame() {
    bind mx = mouse_x()
    bind my = mouse_y()
    bind b = 0.0
    if mouse_down() { bind b = 1.0 }
    if mouse_down_right() { bind b = b + 2.0 }
    if mouse_down_middle() { bind b = b + 4.0 }
    bind changed = b > 0.5 || mouse_scroll() != 0.0
    if mx != st_get("ix_mx", 0.0 - 1.0) || my != st_get("ix_my", 0.0 - 1.0) || b != st_get("ix_b", 0.0) { bind changed = true }
    st_set("ix_mx", mx)
    st_set("ix_my", my)
    st_set("ix_b", b)
    bind t = text_poll()
    if t != "" { st_set("os_text", st_get("os_text", "") + t) bind changed = true }
    if !changed {
        if key_pressed("escape") || key_pressed("enter") || key_pressed("tab") || key_pressed("backspace") || key_pressed("delete") { bind changed = true }
        if key_pressed("up") || key_pressed("down") || key_pressed("left") || key_pressed("right") || key_pressed("home") { bind changed = true }
        if key_pressed("f1") || key_pressed("f2") || key_pressed("f3") || key_pressed("f4") || key_pressed("f5") || key_pressed("f6") || key_pressed("f7") { bind changed = true }
    }
    if tl_playing() || v3_modal_active() || st_get("vc_drag", "") != "" { bind changed = true }
    # after any change keep drawing two more frames so release edges and hover states settle
    if changed { st_set("ix_tail", 2.0) return false }
    bind tail = st_get("ix_tail", 0.0)
    if tail > 0.5 { st_set("ix_tail", tail - 1.0) return false }
    return true
}
# kernel text input hands out only what was typed since the last call: keep our own edit buffer
fn p_text_set(v) {
    st_set("os_tbuf", v)
    st_set("os_text", "")
}
fn p_text_poll() {
    bind pend = st_get("os_text", "") + text_poll()
    st_set("os_text", "")
    bind buf = st_get("os_tbuf", "")
    if key_pressed("backspace") && str_len(buf) > 0.0 { bind buf = str_slice(buf, 0.0, str_len(buf) - 1.0) }
    bind ok_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    bind i = 0.0
    while i < str_len(pend) {
        bind ch = str_slice(pend, i, 1.0)
        if str_find(ok_chars, ch) >= 0.0 { bind buf = buf + ch }
        bind i = i + 1.0
    }
    st_set("os_tbuf", buf)
    return buf
}
fn p_init() { st_set("plat", 1.0) }
fn p_is_os() { return true }
fn p_has_images() { return false }

# even-odd scanline polygon fill (one contour)
fn p_poly(pts, c, a) {
    bind n = floor(len(pts) / 2.0)
    if n < 3.0 { return 0.0 }
    if p_convex(pts, n) { p_poly_convex(pts, n, c, a) return 0.0 }
    bind ymin = 1000000.0
    bind ymax = 0.0 - 1000000.0
    bind i = 0.0
    while i < n {
        bind y = list_get(pts, i * 2.0 + 1.0)
        if y < ymin { bind ymin = y }
        if y > ymax { bind ymax = y }
        bind i = i + 1.0
    }
    bind H = get_height()
    bind y = max(0.0, floor(ymin))
    bind yend = min(H - 1.0, ceil(ymax))
    while y <= yend {
        bind sy = y + 0.5
        # collect crossings (insertion sorted)
        bind xs = list_new()
        bind i = 0.0
        while i < n {
            bind j = i + 1.0
            if j >= n { bind j = 0.0 }
            bind x0 = list_get(pts, i * 2.0)
            bind y0 = list_get(pts, i * 2.0 + 1.0)
            bind x1 = list_get(pts, j * 2.0)
            bind y1 = list_get(pts, j * 2.0 + 1.0)
            if (y0 <= sy && y1 > sy) || (y1 <= sy && y0 > sy) {
                bind xc = x0 + (sy - y0) * (x1 - x0) / (y1 - y0)
                bind xs = list_push(xs, xc)
                # bubble the new crossing into place
                bind k = len(xs) - 1.0
                while k > 0.0 && list_get(xs, k - 1.0) > list_get(xs, k) {
                    bind tmp = list_get(xs, k - 1.0)
                    bind xs = list_set(xs, k - 1.0, list_get(xs, k))
                    bind xs = list_set(xs, k, tmp)
                    bind k = k - 1.0
                }
            }
            bind i = i + 1.0
        }
        bind k = 0.0
        while k + 1.0 < len(xs) {
            bind xa = floor(list_get(xs, k) + 0.5)
            bind xb = floor(list_get(xs, k + 1.0) + 0.5)
            if xb > xa { px_hline(xa, y, xb - xa, c, a) }
            bind k = k + 2.0
        }
        bind y = y + 1.0
    }
}
fn p_convex(pts, n) {
    bind pos = false
    bind neg = false
    bind i = 0.0
    while i < n {
        bind j = (i + 1.0) % n
        bind k = (i + 2.0) % n
        bind ax = list_get(pts, j * 2.0) - list_get(pts, i * 2.0)
        bind ay = list_get(pts, j * 2.0 + 1.0) - list_get(pts, i * 2.0 + 1.0)
        bind bx = list_get(pts, k * 2.0) - list_get(pts, j * 2.0)
        bind by = list_get(pts, k * 2.0 + 1.0) - list_get(pts, j * 2.0 + 1.0)
        bind cr = ax * by - ay * bx
        if cr > 0.0001 { bind pos = true }
        if cr < 0.0 - 0.0001 { bind neg = true }
        bind i = i + 1.0
    }
    return !(pos && neg)
}
# one span per scanline: the leftmost and rightmost edge crossings
fn p_poly_convex(pts, n, c, a) {
    bind ymin = 1000000.0
    bind ymax = 0.0 - 1000000.0
    bind i = 0.0
    while i < n {
        bind y = list_get(pts, i * 2.0 + 1.0)
        if y < ymin { bind ymin = y }
        if y > ymax { bind ymax = y }
        bind i = i + 1.0
    }
    bind y = max(0.0, floor(ymin))
    bind yend = min(get_height() - 1.0, ceil(ymax))
    while y <= yend {
        bind sy = y + 0.5
        bind xl = 1000000.0
        bind xr = 0.0 - 1000000.0
        bind i = 0.0
        while i < n {
            bind j = i + 1.0
            if j >= n { bind j = 0.0 }
            bind y0 = list_get(pts, i * 2.0 + 1.0)
            bind y1 = list_get(pts, j * 2.0 + 1.0)
            if (y0 <= sy && y1 > sy) || (y1 <= sy && y0 > sy) {
                bind x0 = list_get(pts, i * 2.0)
                bind xc = x0 + (sy - y0) * (list_get(pts, j * 2.0) - x0) / (y1 - y0)
                if xc < xl { bind xl = xc }
                if xc > xr { bind xr = xc }
            }
            bind i = i + 1.0
        }
        if xr > xl { px_hline(floor(xl + 0.5), y, floor(xr + 0.5) - floor(xl + 0.5), c, a) }
        bind y = y + 1.0
    }
}
fn p_tri(x0, y0, x1, y1, x2, y2, c, a) { p_poly([x0, y0, x1, y1, x2, y2], c, a) }

fn p_img_new(w, h) { return 0.0 - 1.0 }
fn p_img_set(id, x, y, r, g, b, a) { return 0.0 }
fn p_img_save(id, path) { return 0.0 - 1.0 }
fn p_img_load(path) { return 0.0 - 1.0 }
fn p_img_w(id) { return 0.0 }
fn p_img_h(id) { return 0.0 }
fn p_img_r(id, x, y) { return 0.0 }
fn p_img_g(id, x, y) { return 0.0 }
fn p_img_b(id, x, y) { return 0.0 }
fn p_img_draw(id, x, y, w, h) { return 0.0 }

fn p_dlg_open(title, exts) { return dlg_open(title, exts, st_get("last_dir", "")) }
fn p_dlg_save(title, exts, name) { return dlg_save(title, exts, name, st_get("last_dir", "")) }
fn p_dlg_folder(title) { return dlg_folder(title, st_get("last_dir", "")) }
fn p_write_bytes(path, bytes) { return false }
fn p_shot(name) { screenshot(name) }
fn p_quit() { close_window() }

← Back to index