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/main_os.ling
# GENERATED by tools/make_os_entry.py from main.ling - edit main.ling, then re-run the script.
# LingOS entry: the same LingModeler on the in-kernel Ling runtime (scanline polygons, no image files).
# ━━━ LingModeler — a lightweight vector 3D modeler, 2D vector studio and rigging tool, written in Ling ━━━
# Run (from the LingModeler folder): ling run --interp ling/main.ling or run.bat / run.sh
# Everything draws with ling's pixel + font builtins; the look is LingStationX's design system (ds.ling).
use "plat_os.ling"
use "core.ling"
use "ds.ling"
use "icons.ling"
use "mathx.ling"
use "json.ling"
use "mesh.ling"
use "prims.ling"
use "meshops.ling"
use "view3d.ling"
use "uv.ling"
use "texture.ling"
use "vec2d.ling"
use "anim.ling"
use "timeline.ling"
use "svg.ling"
use "lingfmt.ling"
use "objfmt.ling"
use "gltf.ling"
use "rig.ling"
use "pose.ling"
use "gen.ling"
use "raster.ling"
use "fileio.ling"
use "settings.ling"
use "shell.ling"
use "modals.ling"
use "panels.ling"
use "actions.ling"
bind APP_VERSION = "2030.1.0"
# ── layout ──
bind MENU_H = 24.0
bind TABS_H = 28.0
bind TOOL_H = 34.0
bind STATUS_H = 22.0
bind TIMELINE_H = 132.0
# ── theme (packed 0xRRGGBB). ROYGBIV + the LingStation near-black chrome ──
bind C_BG_APP = 1776411.0 # 27,27,27
bind C_BG_PANEL = 1776411.0 # 27,27,27
bind C_BG_DEEP = 657930.0 # 10,10,10
bind C_BG_SIDE = 1447706.0 # 22,23,26
bind C_BG_VIEW = 790032.0 # 12,14,16
bind C_LINE = 3947580.0 # 60,60,60
bind C_LINE_SOFT = 2763826.0 # 42,44,50
bind C_TEXT = 13488344.0 # 205,208,216
bind C_TEXT_DIM = 9212056.0 # 140,144,152
bind C_TEXT_STRONG = 16119285.0 # 245,245,245
bind C_BTN = 3947580.0 # 60,60,60
bind C_BTN_HOV = 4868682.0 # 74,74,74
bind C_BTN_ACT = 2960685.0 # 45,45,45
bind C_SEL = 23680.0 # 0,92,128
bind C_SEL_ROW = 2503756.0 # 38,52,76
bind C_HOVER_ROW = 1975340.0 # 30,36,44
bind C_RULER = 1184792.0 # 18,20,24
bind C_HDR_LINE = 1842722.0 # 28,30,34
bind C_GRID_MIN = 1710618.0 # 26,26,26
bind C_GRID_MAJ = 2763306.0 # 42,42,42
bind C_PLAYHEAD = 16733766.0 # 255,86,70
bind C_RED = 15551031.0 # 237,74,55
bind C_ORANGE = 16225310.0 # 247,148,30
bind C_YELLOW = 16242758.0 # 247,216,70
bind C_GREEN = 4573296.0 # 69,200,112
bind C_BLUE = 3910379.0 # 59,170,235
bind C_INDIGO = 4875480.0 # 74,100,216
bind C_VIOLET = 10114006.0 # 154,83,214
bind C_MENU_FILE = 15417396.0 # 235,64,52
bind C_MENU_EDIT = 16747560.0 # 255,140,40
bind C_MENU_VIEW = 16108870.0 # 245,205,70
bind C_MENU_TRANS = 5294200.0 # 80,200,120
bind C_MENU_HELP = 7885010.0 # 120,80,210
bind C_POPUP = 1973794.0 # 30,30,34
bind C_POPUP_LINE = 4605518.0 # 70,70,78
bind C_WIRE = 9212056.0 # 140,144,152
bind C_WIRE_DIM = 5592405.0 # 85,85,85
bind C_VERT = 13488344.0
bind C_SEL_EL = 16225310.0 # selected element orange
bind C_ACT_EL = 16777215.0 # active element white
bind C_AXIS_X = 15551031.0
bind C_AXIS_Y = 4573296.0
bind C_AXIS_Z = 3910379.0
bind C_BONE = 10114006.0
bind C_BONE_SEL = 16242758.0
# ── panels layout ──
fn center_view(x, y, w, h) {
bind m = st_get("mode", 0.0)
if m < 4.5 { v3_view(x, y, w, h) }
else if m < 6.5 { vc_view(x, y, w, h) }
else { tx_view(x, y, w, h) }
}
fn draw_frame() {
bind W = get_width()
bind H = get_height()
g_rect(0.0, 0.0, W, H, C_BG_APP)
bind top = MENU_H + TABS_H + TOOL_H
bind bottom = H - STATUS_H
bind m = st_get("mode", 0.0)
bind want_tl = st_get("show_timeline", 1.0) > 0.5 && (m == 4.0 || m == 6.0 || (m == 7.0 && st_get("tx_anim", 0.0) > 0.5))
if want_tl { bind bottom = bottom - TIMELINE_H }
bind left = 0.0
if st_get("show_sidebar", 1.0) > 0.5 {
bind sw = st_get("sb_w", sel(W < 1100.0, 170.0, 210.0))
sidebar(0.0, top, sw, bottom - top)
bind left = sw
}
bind right = W
if st_get("show_props", 1.0) > 0.5 {
bind pw = st_get("props_w", sel(W < 1100.0, 230.0, 262.0))
bind right = W - pw
props_panel(right, top, pw, bottom - top)
bind hot = u_hot(right - 3.0, top, 6.0, bottom - top)
if hot && u_pressed() { u_set_active("pr_resize") }
if u_active() == "pr_resize" && u_down() { st_set("props_w", clampf(W - u_mx(), 200.0, 400.0)) }
if hot || u_active() == "pr_resize" { g_rect(right - 1.0, top, 2.0, bottom - top, C_SEL) }
}
center_view(left, top, right - left, bottom - top)
if want_tl { tl_draw(left, bottom, right - left, TIMELINE_H) }
view_tabs()
toolbar()
status_bar()
}
# tooltips + drag ghosts
fn draw_floating() {
bind tip = st_get("tip", "")
if tip != "" {
bind w = g_text_w(11.0, tip) + 14.0
bind tx = min(u_mx() + 14.0, get_width() - w - 4.0)
bind ty = u_my() + 18.0
if ty + 20.0 > get_height() { bind ty = u_my() - 24.0 }
px_rect(tx, ty, w, 20.0, C_POPUP)
px_outline(tx, ty, w, 20.0, C_POPUP_LINE)
ds_brackets(tx, ty, w, 20.0, 4.0, ds_col("ink"))
g_text(tx + 7.0, ty + 4.0, 11.0, C_TEXT, tip)
}
}
fn autosave_tick() {
bind mins = st_get("autosave", 5.0)
if mins < 0.5 { return 0.0 }
bind now = st_get("now", 0.0)
if st_has("autosave_t") < 0.5 { st_set("autosave_t", now) }
if now - st_get("autosave_t", now) > mins * 60.0 {
st_set("autosave_t", now)
if st_get("dirty", 0.0) > 0.5 && me_count() > 0.5 {
lf_save_lmesh(fs_join(st_get("default_root", "."), "autosave.lmesh"))
st_set("status", "Autosaved to autosave.lmesh")
}
}
}
bind start = do {
bind win_w = 1360.0
bind win_h = 820.0
bind args = get_args()
bind ai = 0.0
while ai < len(args) {
if list_get(args, ai) == "--size" {
bind sz_p = str_split(list_get(args, ai + 1.0), "x")
if len(sz_p) > 1.5 {
bind win_w = max(800.0, to_number(list_get(sz_p, 0)))
bind win_h = max(560.0, to_number(list_get(sz_p, 1)))
}
}
bind ai = ai + 1.0
}
open_window(win_w, win_h, "LingModeler", 1)
px_flat()
p_init()
bind f = font_load("assets/font.ttf")
if f < 0.0 { bind f = font_load("../assets/font.ttf") }
st_set("font", f)
st_set("default_root", fs_cwd())
settings_load()
px_theme(st_get("theme_black", 1.0))
app_init()
bind shot_at = 0.0 - 1.0
bind ai = 0.0
while ai < len(args) {
bind a = list_get(args, ai)
if a == "--shot" { bind shot_at = to_number(list_get(args, ai + 1.0)) }
if a == "--mode" { set_mode(to_number(list_get(args, ai + 1.0))) }
if a == "--name" { st_set("shot_name", list_get(args, ai + 1.0)) }
if a == "--open" { fi_open_path(list_get(args, ai + 1.0)) }
# scripted input: --drive "frame,x,y,down,rdown;..." --scroll "frame,amount;..." --do "frame,action;..."
if a == "--drive" {
st_set("fake_on", 1.0)
bind evs = str_split(list_get(args, ai + 1.0), ";")
bind ei = 0.0
while ei < len(evs) {
bind f4 = str_split(list_get(evs, ei), ",")
st_lpush("dv_f", to_number(list_get(f4, 0)))
st_lpush("dv_x", to_number(list_get(f4, 1)))
st_lpush("dv_y", to_number(list_get(f4, 2)))
st_lpush("dv_d", to_number(list_get(f4, 3)))
st_lpush("dv_r", sel(len(f4) > 4.0, to_number(list_get(f4, 4)), 0.0))
bind ei = ei + 1.0
}
}
if a == "--scroll" {
st_set("fake_on", 1.0)
bind evs = str_split(list_get(args, ai + 1.0), ";")
bind ei = 0.0
while ei < len(evs) {
bind f2 = str_split(list_get(evs, ei), ",")
st_lpush("sv_f", to_number(list_get(f2, 0)))
st_lpush("sv_v", to_number(list_get(f2, 1)))
bind ei = ei + 1.0
}
}
if a == "--do" {
st_set("fake_on", 1.0)
bind evs = str_split(list_get(args, ai + 1.0), ";")
bind ei = 0.0
while ei < len(evs) {
bind f2 = str_split(list_get(evs, ei), ",")
st_lpush("do_f", to_number(list_get(f2, 0)))
bind act = list_get(f2, 1)
bind k = 2.0
while k < len(f2) { bind act = act + "," + list_get(f2, k) bind k = k + 1.0 }
st_lpush("do_a", act)
bind ei = ei + 1.0
}
}
bind ai = ai + 1.0
}
if shot_at < 0.5 && st_get("load_last", 0.0) > 0.5 && st_llen("recent") > 0.5 && file_exists(st_lget("recent", 0.0)) {
fi_open_path(st_lget("recent", 0.0))
}
bind frame = 0.0
bind fps_t = time_now()
bind fps_n = 0.0
while window_is_open() && st_get("quit", 0.0) < 0.5 {
# LingOS frees nothing yet, so an unchanged screen is not rebuilt: present() keeps pumping input
while p_idle_frame() { present() }
st_set("frame", frame)
u_begin()
bind overlay = st_get("menu_open", 0.0 - 1.0) >= 0.0 || st_get("ctx_open", 0.0) > 0.5 || st_get("modal", "") != "" || st_get("cb_open", "") != ""
st_set("blk", sel(overlay, 1.0, 0.0))
tl_tick()
draw_frame()
# the menu bar stays interactive while its own popup is open
st_set("blk", sel(st_get("ctx_open", 0.0) > 0.5 || st_get("modal", "") != "", 1.0, 0.0))
menu_bar()
draw_floating()
st_set("blk", 0.0)
bind act = menu_overlay()
if act != "" { do_action(act) }
bind cact = ctx_overlay()
if cact != "" { do_action(cact) }
modal_overlay()
combo_overlay()
if !overlay { keyboard() }
autosave_tick()
u_end()
present()
p_frame_hook(frame)
bind frame = frame + 1.0
bind fps_n = fps_n + 1.0
if time_now() - fps_t > 0.5 {
st_set("fps", fps_n / (time_now() - fps_t))
bind fps_t = time_now()
bind fps_n = 0.0
}
if shot_at > 0.0 && frame == shot_at {
p_shot(st_get("shot_name", "lm"))
close_window()
}
}
if shot_at < 0.5 { settings_save() }
}