灵符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/timeline.ling
# ━━━ timeline.ling — shared frame timeline (pose animation, 2D vector animation, animated textures) ━━━

fn tl_init() {
    st_set("tl_frame", 0.0)
    st_set("tl_start", 0.0)
    st_set("tl_end", 48.0)
    st_set("tl_fps", 24.0)
    st_set("tl_playing", 0.0)
    st_set("tl_loop", 1.0)
    st_set("tl_dirty", 1.0)
    st_set("tl_last", 0.0 - 1.0)
}
fn tl_frame() { return st_get("tl_frame", 0.0) }
fn tl_fps() { return max(1.0, st_get("tl_fps", 24.0)) }
fn tl_playing() { return st_get("tl_playing", 0.0) > 0.5 }
fn tl_set_frame(f) {
    st_set("tl_frame", clampf(f, st_get("tl_start", 0.0), max(st_get("tl_start", 0.0), st_get("tl_end", 48.0))))
    tl_mark_dirty()
}
fn tl_mark_dirty() { st_set("tl_dirty", 1.0) }
fn tl_toggle_play() {
    st_set("tl_playing", 1.0 - st_get("tl_playing", 0.0))
    if tl_playing() { st_set("status", "Playing - Space pauses") }
}

# advance when playing, then evaluate the animation for the current mode
fn tl_tick() {
    bind m = st_get("mode", 0.0)
    if tl_playing() {
        bind f = st_get("tl_frame", 0.0) + st_get("dt", 0.016) * tl_fps()
        bind e = st_get("tl_end", 48.0)
        bind s = st_get("tl_start", 0.0)
        if f > e { if st_get("tl_loop", 1.0) > 0.5 { bind f = s + (f - e) % max(1.0, e - s) } else { bind f = e st_set("tl_playing", 0.0) } }
        st_set("tl_frame", f)
        st_set("tl_dirty", 1.0)
    }
    if st_get("tl_dirty", 0.0) < 0.5 { return 0.0 }
    st_set("tl_dirty", 0.0)
    bind f = st_get("tl_frame", 0.0)
    if m < 4.5 {
        bind ids = me_ids()
        bind i = 0.0
        while i < len(ids) {
            bind o = list_get(ids, i)
            if ps_cur_action(o) >= 0.0 && rg_count(o) > 0.5 { ps_eval(o, f) }
            bind i = i + 1.0
        }
    } else if m < 6.5 {
        an_eval(f)
    } else {
        st_set("tx_time", f / tl_fps())
    }
}

# key frames to show for the current selection
fn tl_keys_for_mode() {
    bind m = st_get("mode", 0.0)
    if m < 4.5 { bind o = me_sel_o() if o >= 0.0 { return ps_key_frames(o) } return [] }
    if m < 6.5 { return an_key_frames() }
    return []
}

fn tl_draw(x, y, w, h) {
    px_rect(x, y, w, h, C_BG_SIDE)
    u_hsep(x, y, w)
    bind m = st_get("mode", 0.0)
    bind by = y + 6.0
    bind bx = x + 8.0
    bind playing = tl_playing()
    if u_icon_button("tl_first", bx, by, 26.0, 22.0, "skip_l", 11.0, C_TEXT, false) { tl_set_frame(st_get("tl_start", 0.0)) }
    if u_icon_button("tl_play", bx + 28.0, by, 34.0, 22.0, sel(playing, "pause", "play"), 13.0, sel(playing, C_YELLOW, C_GREEN), playing) { tl_toggle_play() }
    if u_icon_button("tl_last", bx + 64.0, by, 26.0, 22.0, "skip_r", 11.0, C_TEXT, false) { tl_set_frame(st_get("tl_end", 48.0)) }
    bind bx = bx + 98.0
    bind nv = u_dragval("tl_fr", bx, by, 74.0, 22.0, floor(st_get("tl_frame", 0.0)), 0.0, 100000.0, 0.25, "Frame " + fmt0(st_get("tl_frame", 0.0)))
    if u_has(nv) { tl_set_frame(round(nv)) }
    bind bx = bx + 80.0
    bind nv = u_dragval("tl_st", bx, by, 64.0, 22.0, st_get("tl_start", 0.0), 0.0, 100000.0, 0.25, "Start " + fmt0(st_get("tl_start", 0.0)))
    if u_has(nv) { st_set("tl_start", min(round(nv), st_get("tl_end", 48.0) - 1.0)) tl_mark_dirty() }
    bind bx = bx + 68.0
    bind nv = u_dragval("tl_en", bx, by, 64.0, 22.0, st_get("tl_end", 48.0), 1.0, 100000.0, 0.25, "End " + fmt0(st_get("tl_end", 48.0)))
    if u_has(nv) { st_set("tl_end", max(round(nv), st_get("tl_start", 0.0) + 1.0)) tl_mark_dirty() }
    bind bx = bx + 68.0
    bind nv = u_dragval("tl_fps", bx, by, 64.0, 22.0, st_get("tl_fps", 24.0), 1.0, 120.0, 0.1, fmt0(st_get("tl_fps", 24.0)) + " fps")
    if u_has(nv) { st_set("tl_fps", round(nv)) st_set("an_fps", round(nv)) }
    bind bx = bx + 72.0
    if u_chip("tl_loop", bx, by + 1.0, 46.0, 20.0, "Loop", st_get("tl_loop", 1.0) > 0.5, C_BLUE) { st_set("tl_loop", 1.0 - st_get("tl_loop", 1.0)) }
    if u_chip("tl_autokey", bx + 50.0, by + 1.0, 66.0, 20.0, "Auto key", st_get("autokey", 0.0) > 0.5, C_RED) { st_set("autokey", 1.0 - st_get("autokey", 0.0)) }
    bind bx = bx + 124.0
    if m == 4.0 || m == 6.0 {
        if u_button("tl_key", bx, by, 64.0, 22.0, "Key (I)", C_BLUE) { if m == 4.0 { ps_key() } else { an_key() } }
        if u_button("tl_unkey", bx + 68.0, by, 64.0, 22.0, "Unkey", 0.0) { if m == 4.0 { ps_unkey() } else { an_unkey() } }
        if m == 6.0 { if u_button("tl_shape", bx + 136.0, by, 92.0, 22.0, "Shape key (K)", 0.0) { an_shapekey() } }
    }
    # ruler
    bind ry = y + 36.0
    bind rh = h - 44.0
    bind rx = x + 8.0
    bind rw = w - 16.0
    px_rect(rx, ry, rw, rh, C_BG_DEEP)
    px_outline(rx, ry, rw, rh, C_LINE_SOFT)
    bind s = st_get("tl_start", 0.0)
    bind e = max(s + 1.0, st_get("tl_end", 48.0))
    bind span = e - s
    bind pxf = rw / span
    bind step = 1.0
    if pxf < 40.0 { bind step = 5.0 }
    if pxf < 8.0 { bind step = 10.0 }
    if pxf < 3.0 { bind step = 50.0 }
    if pxf < 0.6 { bind step = 250.0 }
    bind f = ceil(s / step) * step
    while f <= e {
        bind fx = rx + (f - s) * pxf
        bind major = f % (step * 5.0) == 0.0
        px_line(fx, ry + sel(major, 2.0, rh - 10.0), fx, ry + rh - 1.0, sel(major, C_GRID_MAJ, C_GRID_MIN), 1.0)
        if major || pxf > 20.0 { g_text(fx + 3.0, ry + 3.0, 10.0, C_TEXT_DIM, fmt0(f)) }
        bind f = f + step
    }
    # keys
    bind keys = tl_keys_for_mode()
    bind cur = st_get("tl_frame", 0.0)
    bind ky = ry + rh * 0.55
    bind i = 0.0
    while i < len(keys) {
        bind kf = list_get(keys, i)
        if kf >= s && kf <= e {
            bind kx = rx + (kf - s) * pxf
            bind on = abs(kf - cur) < 0.5
            bind col = sel(on, C_TEXT_STRONG, C_YELLOW)
            g_tri(kx, ky - 6.0, kx + 6.0, ky, kx, ky + 6.0, col)
            g_tri(kx, ky - 6.0, kx, ky + 6.0, kx - 6.0, ky, col)
        }
        bind i = i + 1.0
    }
    # playhead
    bind px2 = rx + clampf((cur - s) * pxf, 0.0, rw)
    px_line(px2, ry, px2, ry + rh, C_PLAYHEAD, 1.0)
    px_rect(px2 - 14.0, ry + rh - 14.0, 28.0, 13.0, C_PLAYHEAD)
    g_text_c(px2, ry + rh - 13.0, 10.0, C_BG_DEEP, fmt0(cur))
    # scrub
    bind hot = u_hot(rx, ry, rw, rh)
    if hot && u_pressed() { u_set_active("tl_scrub") }
    if u_active() == "tl_scrub" && u_down() {
        bind nf = s + (u_mx() - rx) / pxf
        tl_set_frame(round(clampf(nf, s, e)))
    }
    if hot && u_scroll() != 0.0 && st_get("wheel_used", 0.0) < 0.5 { tl_set_frame(cur + sel(u_scroll() > 0.0, 1.0, 0.0 - 1.0)) st_set("wheel_used", 1.0) }
    # mode hint
    bind hint = sel(m == 4.0, "Pose keys of the selected bones", sel(m == 6.0, "Keys of the selected items", "Texture time"))
    g_text_r(x + w - 10.0, by + 5.0, 11.0, C_TEXT_DIM, hint)
}

← Back to index