灵符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/uv.ling
# ━━━ uv.ling — UV projections (box / planar / cylinder / sphere / reset) and a small UV panel ━━━

fn uv_reset(o) {
    # each face gets a unit square (first three corners of an n-gon fanned round the square)
    bind fi = st_get(mk(o, "fi"), []) bind fs = st_get(mk(o, "fs"), []) bind fc = st_get(mk(o, "fc"), [])
    bind uv = me_zeros(len(fi) * 2.0)
    bind f = 0.0
    while f < len(fc) {
        bind s = list_get(fs, f) bind c = list_get(fc, f)
        bind k = 0.0
        while k < c {
            bind a = k / c * 6.28318530718 - 2.35619449
            bind uv = list_set(uv, (s + k) * 2.0, 0.5 + cos(a) * 0.5)
            bind uv = list_set(uv, (s + k) * 2.0 + 1.0, 0.5 + sin(a) * 0.5)
            bind k = k + 1.0
        }
        bind f = f + 1.0
    }
    st_set(mk(o, "uv"), uv)
}

# normalise a box of uvs into 0..1 using the object's local bounds
fn uv_bounds(o) {
    bind b = me_sel_bounds_local(o, false)
    if len(b) < 0.5 { return [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] }
    bind k = 0.0
    while k < 3.0 { if list_get(b, k + 3.0) - list_get(b, k) < 0.000001 { bind b = list_set(b, k + 3.0, list_get(b, k) + 1.0) } bind k = k + 1.0 }
    return b
}

fn uv_box(o) {
    bind vx = st_get(mk(o, "vx"), []) bind vy = st_get(mk(o, "vy"), []) bind vz = st_get(mk(o, "vz"), [])
    bind fi = st_get(mk(o, "fi"), []) bind fs = st_get(mk(o, "fs"), []) bind fc = st_get(mk(o, "fc"), [])
    bind b = uv_bounds(o)
    bind sx = list_get(b, 3) - list_get(b, 0) bind sy = list_get(b, 4) - list_get(b, 1) bind sz = list_get(b, 5) - list_get(b, 2)
    bind sc = max(sx, max(sy, sz))
    bind uv = me_zeros(len(fi) * 2.0)
    bind f = 0.0
    while f < len(fc) {
        bind s = list_get(fs, f) bind c = list_get(fc, f)
        bind n = me_face_normal(vx, vy, vz, fi, s, c)
        bind ax = abs(list_get(n, 0)) bind ay = abs(list_get(n, 1)) bind az = abs(list_get(n, 2))
        bind k = 0.0
        while k < c {
            bind v = list_get(fi, s + k)
            bind px = (list_get(vx, v) - list_get(b, 0)) / sc
            bind py = (list_get(vy, v) - list_get(b, 1)) / sc
            bind pz = (list_get(vz, v) - list_get(b, 2)) / sc
            bind u = px bind w = py
            if ax >= ay && ax >= az { bind u = pz bind w = py }
            else if ay >= ax && ay >= az { bind u = px bind w = pz }
            bind uv = list_set(uv, (s + k) * 2.0, u)
            bind uv = list_set(uv, (s + k) * 2.0 + 1.0, w)
            bind k = k + 1.0
        }
        bind f = f + 1.0
    }
    st_set(mk(o, "uv"), uv)
    st_set("dirty", 1.0)
}

# planar from the current view (camera right/up)
fn uv_planar(o) {
    bind vx = st_get(mk(o, "vx"), []) bind vy = st_get(mk(o, "vy"), []) bind vz = st_get(mk(o, "vz"), [])
    bind fi = st_get(mk(o, "fi"), [])
    bind m = me_matrix(o)
    bind r = v3_cam_right() bind up = v3_cam_up()
    bind us = list_new() bind ws = list_new()
    bind umin = 1000000.0 bind umax = 0.0 - 1000000.0 bind wmin = 1000000.0 bind wmax = 0.0 - 1000000.0
    bind i = 0.0
    while i < len(vx) {
        bind p = m_apply(m, [list_get(vx, i), list_get(vy, i), list_get(vz, i)])
        bind u = v3_dot(p, r) bind w = v3_dot(p, up)
        bind us = list_push(us, u) bind ws = list_push(ws, w)
        if u < umin { bind umin = u } if u > umax { bind umax = u }
        if w < wmin { bind wmin = w } if w > wmax { bind wmax = w }
        bind i = i + 1.0
    }
    bind su = max(0.000001, umax - umin) bind sw = max(0.000001, wmax - wmin)
    bind uv = me_zeros(len(fi) * 2.0)
    bind i = 0.0
    while i < len(fi) {
        bind v = list_get(fi, i)
        bind uv = list_set(uv, i * 2.0, (list_get(us, v) - umin) / su)
        bind uv = list_set(uv, i * 2.0 + 1.0, (list_get(ws, v) - wmin) / sw)
        bind i = i + 1.0
    }
    st_set(mk(o, "uv"), uv)
    st_set("dirty", 1.0)
}

fn uv_cyl(o) {
    bind vx = st_get(mk(o, "vx"), []) bind vy = st_get(mk(o, "vy"), []) bind vz = st_get(mk(o, "vz"), [])
    bind fi = st_get(mk(o, "fi"), []) bind fs = st_get(mk(o, "fs"), []) bind fc = st_get(mk(o, "fc"), [])
    bind b = uv_bounds(o)
    bind cx = (list_get(b, 0) + list_get(b, 3)) * 0.5 bind cz = (list_get(b, 2) + list_get(b, 5)) * 0.5
    bind ymin = list_get(b, 1) bind sy = list_get(b, 4) - list_get(b, 1)
    bind uv = me_zeros(len(fi) * 2.0)
    bind f = 0.0
    while f < len(fc) {
        bind s = list_get(fs, f) bind c = list_get(fc, f)
        bind first = 0.0
        bind k = 0.0
        while k < c {
            bind v = list_get(fi, s + k)
            bind u = mx_atan2(list_get(vz, v) - cz, list_get(vx, v) - cx) / 6.28318530718 + 0.5
            if k == 0.0 { bind first = u }
            # keep the seam from wrapping inside one face
            if u - first > 0.5 { bind u = u - 1.0 }
            if first - u > 0.5 { bind u = u + 1.0 }
            bind uv = list_set(uv, (s + k) * 2.0, u)
            bind uv = list_set(uv, (s + k) * 2.0 + 1.0, (list_get(vy, v) - ymin) / sy)
            bind k = k + 1.0
        }
        bind f = f + 1.0
    }
    st_set(mk(o, "uv"), uv)
    st_set("dirty", 1.0)
}

fn uv_sphere(o) {
    bind vx = st_get(mk(o, "vx"), []) bind vy = st_get(mk(o, "vy"), []) bind vz = st_get(mk(o, "vz"), [])
    bind fi = st_get(mk(o, "fi"), []) bind fs = st_get(mk(o, "fs"), []) bind fc = st_get(mk(o, "fc"), [])
    bind b = uv_bounds(o)
    bind cx = (list_get(b, 0) + list_get(b, 3)) * 0.5 bind cy = (list_get(b, 1) + list_get(b, 4)) * 0.5 bind cz = (list_get(b, 2) + list_get(b, 5)) * 0.5
    bind uv = me_zeros(len(fi) * 2.0)
    bind f = 0.0
    while f < len(fc) {
        bind s = list_get(fs, f) bind c = list_get(fc, f)
        bind first = 0.0
        bind k = 0.0
        while k < c {
            bind v = list_get(fi, s + k)
            bind dx = list_get(vx, v) - cx bind dy = list_get(vy, v) - cy bind dz = list_get(vz, v) - cz
            bind l = max(0.000001, sqrt(dx * dx + dy * dy + dz * dz))
            bind u = mx_atan2(dz, dx) / 6.28318530718 + 0.5
            if k == 0.0 { bind first = u }
            if u - first > 0.5 { bind u = u - 1.0 }
            if first - u > 0.5 { bind u = u + 1.0 }
            bind uv = list_set(uv, (s + k) * 2.0, u)
            bind uv = list_set(uv, (s + k) * 2.0 + 1.0, mx_acos(clampf(0.0 - dy / l, 0.0 - 1.0, 1.0)) / 3.14159265359)
            bind k = k + 1.0
        }
        bind f = f + 1.0
    }
    st_set(mk(o, "uv"), uv)
    st_set("dirty", 1.0)
}

# draw the UV layout of an object in a square panel
fn uv_panel(o, x, y, w, h) {
    bind s = min(w, h) - 8.0
    bind ox = x + 4.0 bind oy = y + 4.0
    px_rect(ox, oy, s, s, C_BG_DEEP)
    px_outline(ox, oy, s, s, ds_col("ink"), 0.6)
    bind k = 1.0
    while k < 4.0 { px_line(ox + s * k / 4.0, oy, ox + s * k / 4.0, oy + s, C_GRID_MAJ, 0.6) px_line(ox, oy + s * k / 4.0, ox + s, oy + s * k / 4.0, C_GRID_MAJ, 0.6) bind k = k + 1.0 }
    if o < 0.0 { return 0.0 }
    bind fi = st_get(mk(o, "fi"), []) bind fs = st_get(mk(o, "fs"), []) bind fc = st_get(mk(o, "fc"), [])
    bind uv = st_get(mk(o, "uv"), [])
    bind fsel = st_get(mk(o, "fsel"), [])
    if len(uv) < len(fi) * 2.0 { return 0.0 }
    bind f = 0.0
    while f < len(fc) {
        bind st = list_get(fs, f) bind c = list_get(fc, f)
        bind col = sel(f < len(fsel) && list_get(fsel, f) > 0.5, C_SEL_EL, C_BLUE)
        bind k = 0.0
        while k < c {
            bind a = st + k bind b = st + (k + 1.0) % c
            px_line(ox + list_get(uv, a * 2.0) * s, oy + (1.0 - list_get(uv, a * 2.0 + 1.0)) * s, ox + list_get(uv, b * 2.0) * s, oy + (1.0 - list_get(uv, b * 2.0 + 1.0)) * s, col, 0.8)
            bind k = k + 1.0
        }
        bind f = f + 1.0
    }
}

← Back to index