lingmodeler
↓ 0 downloadsLingModeler: 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
Keywords 3d, modeling, vector, svg, gltf, obj, rigging, animation, textures, ling
lingfu install lingmodelerCode / 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
}
}