灵符lingfu

lingdev

↓ 0 downloads

LingDev: a Godot / Unity-style game editor written in Ling. 2-D and 3-D scenes, Flash-style timeline and cutscenes, HUDs over 3-D, Ling scripting, audio / music / MIDI, builds for Windows, Linux, macOS, web, Raspberry Pi and LingOS. Ships with Ling Agent (an N64-style spy FPS), Ling Breaker (a 3-D breakout) and Moko's Picture Book (a kawaii storybook platformer).

Windows · x86-64Linux · x86-64LingOS · x86-64LingOS · Pi 5 ARM64Web · WASM
Keywords game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdev
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (136)

ling/rt/rt_sw3d.ling
# rt_sw3d.ling - a small software 3-D renderer over the 2-D pixel builtins (px_line / spans).
# It is what the editor viewport draws with on every runtime, and what LingOS builds (which have
# no native 3-D) render games with. Wireframe primitives, flat-lit solid faces sorted back to
# front, grid, axes, gizmos and labels, all projected through rt_cam.

# rotate a local point by Euler angles applied X -> Y -> Z (the renderer's shape convention)
fn r3_rot(x, y, z, rx, ry, rz) {
    bind cx = cos(rx)
    bind sx = sin(rx)
    bind y1 = y * cx - z * sx
    bind z1 = y * sx + z * cx
    bind cy = cos(ry)
    bind sy = sin(ry)
    bind x2 = x * cy + z1 * sy
    bind z2 = 0.0 - x * sy + z1 * cy
    bind cz = cos(rz)
    bind sz = sin(rz)
    bind x3 = x2 * cz - y1 * sz
    bind y3 = x2 * sz + y1 * cz
    return [x3, y3, z2]
}
# local point (lx,ly,lz) of a node with centre c, half size s, rotation r -> world
fn r3_local(lx, ly, lz, cx, cy, cz, sx, sy, sz, rx, ry, rz) {
    bind p = r3_rot(lx * sx, ly * sy, lz * sz, rx, ry, rz)
    return [list_get(p, 0) + cx, list_get(p, 1) + cy, list_get(p, 2) + cz]
}

# ── lines ────────────────────────────────────────────────────────────────────
# a world-space segment, clipped against the near plane, drawn with alpha a
fn sw_line(x0, y0, z0, x1, y1, z1, c, a) {
    bind p0 = cam_to_view(x0, y0, z0)
    bind p1 = cam_to_view(x1, y1, z1)
    bind near = 0.08 - st_get("cam_zd", 2.0)
    bind az = list_get(p0, 2)
    bind bz = list_get(p1, 2)
    if az < near && bz < near { return 0.0 }
    bind ax = list_get(p0, 0)
    bind ay = list_get(p0, 1)
    bind bx = list_get(p1, 0)
    bind by = list_get(p1, 1)
    if az < near {
        bind t = (near - az) / (bz - az)
        bind ax = ax + (bx - ax) * t
        bind ay = ay + (by - ay) * t
        bind az = near
    }
    if bz < near {
        bind t = (near - bz) / (az - bz)
        bind bx = bx + (ax - bx) * t
        bind by = by + (ay - by) * t
        bind bz = near
    }
    bind f = st_get("vp_f", 600.0)
    bind cx = st_get("vp_cx", 400.0)
    bind cy = st_get("vp_cy", 300.0)
    bind zd = st_get("cam_zd", 2.0)
    bind sx0 = cx + f * ax / (az + zd)
    bind sy0 = cy + f * ay / (az + zd)
    bind sx1 = cx + f * bx / (bz + zd)
    bind sy1 = cy + f * by / (bz + zd)
    # cull segments far outside the viewport
    bind vx = st_get("vp_x", 0.0)
    bind vy = st_get("vp_y", 0.0)
    bind vw = st_get("vp_w", 800.0)
    bind vh = st_get("vp_h", 600.0)
    if (sx0 < vx - 2000.0 && sx1 < vx - 2000.0) || (sx0 > vx + vw + 2000.0 && sx1 > vx + vw + 2000.0) { return 0.0 }
    if (sy0 < vy - 2000.0 && sy1 < vy - 2000.0) || (sy0 > vy + vh + 2000.0 && sy1 > vy + vh + 2000.0) { return 0.0 }
    px_line(sx0, sy0, sx1, sy1, c, a)
    return 0.0
}
fn sw_point(x, y, z, r, c) {
    bind p = cam_project(x, y, z)
    if !cam_visible(list_get(p, 2)) { return 0.0 }
    px_disc(list_get(p, 0), list_get(p, 1), r, c)
    return 0.0
}
fn sw_label(x, y, z, size, c, text) {
    bind p = cam_project(x, y, z)
    if !cam_visible(list_get(p, 2)) { return 0.0 }
    g_text(list_get(p, 0) + 6.0, list_get(p, 1) - size * 0.5, size, c, text)
    return 0.0
}

# ── wire primitives (centre, half size, rotation) ────────────────────────────
fn sw_box(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    bind p0 = r3_local(0.0 - 1.0, 0.0 - 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p1 = r3_local(1.0, 0.0 - 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p2 = r3_local(1.0, 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p3 = r3_local(0.0 - 1.0, 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p4 = r3_local(0.0 - 1.0, 0.0 - 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p5 = r3_local(1.0, 0.0 - 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p6 = r3_local(1.0, 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p7 = r3_local(0.0 - 1.0, 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    sw_seg(p0, p1, c, a)  sw_seg(p1, p2, c, a)  sw_seg(p2, p3, c, a)  sw_seg(p3, p0, c, a)
    sw_seg(p4, p5, c, a)  sw_seg(p5, p6, c, a)  sw_seg(p6, p7, c, a)  sw_seg(p7, p4, c, a)
    sw_seg(p0, p4, c, a)  sw_seg(p1, p5, c, a)  sw_seg(p2, p6, c, a)  sw_seg(p3, p7, c, a)
}
fn sw_seg(p, q, c, a) {
    sw_line(list_get(p, 0), list_get(p, 1), list_get(p, 2), list_get(q, 0), list_get(q, 1), list_get(q, 2), c, a)
}
# a ring in a local plane: axis 0 = around x, 1 = around y, 2 = around z; `off` = offset along the axis
fn sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, axis, off, rad, segs, c, a) {
    bind i = 0.0
    bind prev = []
    bind first = []
    while i <= segs {
        bind t = i / segs * 6.2831853
        bind u = cos(t) * rad
        bind v = sin(t) * rad
        bind p = []
        if axis == 1.0 { bind p = r3_local(u, off, v, cx, cy, cz, sx, sy, sz, rx, ry, rz) }
        else if axis == 0.0 { bind p = r3_local(off, u, v, cx, cy, cz, sx, sy, sz, rx, ry, rz) }
        else { bind p = r3_local(u, v, off, cx, cy, cz, sx, sy, sz, rx, ry, rz) }
        if i > 0.0 { sw_seg(prev, p, c, a) }
        bind prev = p
        bind i = i + 1.0
    }
}
fn sw_sphere(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0, 1.0, 24.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 0.0, 0.0, 1.0, 24.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 2.0, 0.0, 1.0, 24.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0 - 0.7, 0.71, 16.0, c, a * 0.6)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.7, 0.71, 16.0, c, a * 0.6)
}
fn sw_cylinder(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0 - 1.0, 1.0, 20.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 1.0, 1.0, 20.0, c, a)
    bind k = 0.0
    while k < 4.0 {
        bind t = k / 4.0 * 6.2831853
        sw_seg(r3_local(cos(t), 0.0 - 1.0, sin(t), cx, cy, cz, sx, sy, sz, rx, ry, rz), r3_local(cos(t), 1.0, sin(t), cx, cy, cz, sx, sy, sz, rx, ry, rz), c, a)
        bind k = k + 1.0
    }
}
fn sw_cone(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 1.0, 1.0, 20.0, c, a)
    bind apex = r3_local(0.0, 0.0 - 1.0, 0.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind k = 0.0
    while k < 4.0 {
        bind t = k / 4.0 * 6.2831853
        sw_seg(r3_local(cos(t), 1.0, sin(t), cx, cy, cz, sx, sy, sz, rx, ry, rz), apex, c, a)
        bind k = k + 1.0
    }
}
fn sw_capsule(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    sw_cylinder(cx, cy, cz, sx, sy * 0.5, sz, rx, ry, rz, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 2.0, 0.0, 1.0, 20.0, c, a * 0.7)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 0.0, 0.0, 1.0, 20.0, c, a * 0.7)
}
fn sw_torus(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0, 1.0, 28.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0, 0.3, 20.0, c, a)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.35, 0.65, 24.0, c, a * 0.6)
    sw_ring(cx, cy, cz, sx, sy, sz, rx, ry, rz, 1.0, 0.0 - 0.35, 0.65, 24.0, c, a * 0.6)
}
fn sw_plane(cx, cy, cz, sx, sz, ry, c, a) {
    bind p0 = r3_local(0.0 - 1.0, 0.0, 0.0 - 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p1 = r3_local(1.0, 0.0, 0.0 - 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p2 = r3_local(1.0, 0.0, 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p3 = r3_local(0.0 - 1.0, 0.0, 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    sw_seg(p0, p1, c, a)  sw_seg(p1, p2, c, a)  sw_seg(p2, p3, c, a)  sw_seg(p3, p0, c, a)
    sw_seg(p0, p2, c, a * 0.35)  sw_seg(p1, p3, c, a * 0.35)
}

# ── scene furniture ──────────────────────────────────────────────────────────
fn sw_grid(half, step, y, cmaj, cmin) {
    bind k = 0.0 - half
    while k <= half {
        bind major = abs(k % (step * 5.0)) < 0.001
        bind c = sel(major, cmaj, cmin)
        bind a = sel(major, 0.9, 0.5)
        if abs(k) < 0.001 { bind a = 0.0 }
        sw_line(k, y, 0.0 - half, k, y, half, c, a)
        sw_line(0.0 - half, y, k, half, y, k, c, a)
        bind k = k + step
    }
    sw_line(0.0 - half, y, 0.0, half, y, 0.0, C_AXIS_X, 0.8)
    sw_line(0.0, y, 0.0 - half, 0.0, y, half, C_AXIS_Z, 0.8)
}
fn sw_axes(x, y, z, ln) {
    sw_line(x, y, z, x + ln, y, z, C_AXIS_X, 1.0)
    sw_line(x, y, z, x, y - ln, z, C_AXIS_Y, 1.0)
    sw_line(x, y, z, x, y, z + ln, C_AXIS_Z, 1.0)
}
# camera frustum gizmo
fn sw_camera_gizmo(cx, cy, cz, yaw, pitch, c, a) {
    bind cp = cos(pitch)
    bind sp = sin(pitch)
    bind fx = sin(yaw) * cp
    bind fy = 0.0 - sp
    bind fz = cos(yaw) * cp
    bind rx = cos(yaw)
    bind rz = 0.0 - sin(yaw)
    bind ux = 0.0 - sin(yaw) * sp
    bind uy = 0.0 - cp
    bind uz = 0.0 - cos(yaw) * sp
    bind d = 1.6
    bind hw = 0.9
    bind hh = 0.55
    bind ex = cx + fx * d
    bind ey = cy + fy * d
    bind ez = cz + fz * d
    bind c0x = ex - rx * hw - ux * hh
    bind c0y = ey - uy * hh
    bind c0z = ez - rz * hw - uz * hh
    bind c1x = ex + rx * hw - ux * hh
    bind c1y = ey - uy * hh
    bind c1z = ez + rz * hw - uz * hh
    bind c2x = ex + rx * hw + ux * hh
    bind c2y = ey + uy * hh
    bind c2z = ez + rz * hw + uz * hh
    bind c3x = ex - rx * hw + ux * hh
    bind c3y = ey + uy * hh
    bind c3z = ez - rz * hw + uz * hh
    sw_line(cx, cy, cz, c0x, c0y, c0z, c, a)
    sw_line(cx, cy, cz, c1x, c1y, c1z, c, a)
    sw_line(cx, cy, cz, c2x, c2y, c2z, c, a)
    sw_line(cx, cy, cz, c3x, c3y, c3z, c, a)
    sw_line(c0x, c0y, c0z, c1x, c1y, c1z, c, a)
    sw_line(c1x, c1y, c1z, c2x, c2y, c2z, c, a)
    sw_line(c2x, c2y, c2z, c3x, c3y, c3z, c, a)
    sw_line(c3x, c3y, c3z, c0x, c0y, c0z, c, a)
    # "up" tab
    sw_line(c2x, c2y, c2z, ex + ux * hh * 1.6, ey + uy * hh * 1.6, ez + uz * hh * 1.6, c, a)
    sw_line(c3x, c3y, c3z, ex + ux * hh * 1.6, ey + uy * hh * 1.6, ez + uz * hh * 1.6, c, a)
    sw_box(cx, cy, cz, 0.35, 0.25, 0.25, 0.0, yaw, 0.0, c, a)
}
fn sw_light_gizmo(cx, cy, cz, radius, c, a) {
    sw_sphere(cx, cy, cz, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0, c, a)
    sw_ring(cx, cy, cz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, radius, 32.0, c, a * 0.25)
    bind k = 0.0
    while k < 8.0 {
        bind t = k / 8.0 * 6.2831853
        sw_line(cx + cos(t) * 0.45, cy, cz + sin(t) * 0.45, cx + cos(t) * 0.8, cy, cz + sin(t) * 0.8, c, a)
        bind k = k + 1.0
    }
    sw_line(cx, cy - 0.45, cz, cx, cy - 0.8, cz, c, a)
    sw_line(cx, cy + 0.45, cz, cx, cy + 0.8, cz, c, a)
}
fn sw_spawn_gizmo(cx, cy, cz, yaw, c, a) {
    sw_line(cx, cy, cz, cx, cy - 1.6, cz, c, a)
    bind fx = sin(yaw)
    bind fz = cos(yaw)
    bind rx = cos(yaw)
    bind rz = 0.0 - sin(yaw)
    sw_line(cx, cy - 1.6, cz, cx + fx * 0.9, cy - 1.3, cz + fz * 0.9, c, a)
    sw_line(cx + fx * 0.9, cy - 1.3, cz + fz * 0.9, cx, cy - 1.0, cz, c, a)
    sw_ring(cx, cy, cz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.5, 16.0, c, a)
    sw_line(cx, cy, cz, cx + fx * 1.2, cy, cz + fz * 1.2, c, a)
    sw_line(cx + fx * 1.2, cy, cz + fz * 1.2, cx + fx * 0.8 + rx * 0.25, cy, cz + fz * 0.8 + rz * 0.25, c, a)
    sw_line(cx + fx * 1.2, cy, cz + fz * 1.2, cx + fx * 0.8 - rx * 0.25, cy, cz + fz * 0.8 - rz * 0.25, c, a)
}
fn sw_audio_gizmo(cx, cy, cz, radius, c, a) {
    sw_box(cx, cy, cz, 0.25, 0.3, 0.2, 0.0, 0.0, 0.0, c, a)
    sw_ring(cx, cy, cz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.7, 16.0, c, a * 0.7)
    sw_ring(cx, cy, cz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, max(0.8, radius), 24.0, c, a * 0.3)
}
fn sw_actor_gizmo(cx, cy, cz, yaw, h, c, a) {
    sw_capsule(cx, cy - h * 0.5, cz, h * 0.22, h * 0.5, h * 0.22, 0.0, yaw, 0.0, c, a)
    bind fx = sin(yaw)
    bind fz = cos(yaw)
    sw_line(cx, cy - h * 0.5, cz, cx + fx * h * 0.5, cy - h * 0.5, cz + fz * h * 0.5, c, a)
}

# ── solid faces, sorted back to front ────────────────────────────────────────
# faces are buffered as records in the store then flushed once, farthest first
fn sw_solid_begin() {
    st_lclear("sf_d")
    st_lclear("sf_rec")
}
# a quad face in world space with a flat colour; lit by the direction light
fn sw_face(x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3, c, a) {
    bind p0 = cam_project(x0, y0, z0)
    bind p1 = cam_project(x1, y1, z1)
    bind p2 = cam_project(x2, y2, z2)
    bind p3 = cam_project(x3, y3, z3)
    bind near = 0.08 - st_get("cam_zd", 2.0)
    if list_get(p0, 2) < near || list_get(p1, 2) < near || list_get(p2, 2) < near || list_get(p3, 2) < near { return 0.0 }
    # normal (for lighting + back-face dimming)
    bind ux = x1 - x0
    bind uy = y1 - y0
    bind uz = z1 - z0
    bind vx = x3 - x0
    bind vy = y3 - y0
    bind vz = z3 - z0
    bind nx = uy * vz - uz * vy
    bind ny = uz * vx - ux * vz
    bind nz = ux * vy - uy * vx
    bind nl = sqrt(nx * nx + ny * ny + nz * nz)
    if nl < 0.000001 { return 0.0 }
    bind nx = nx / nl
    bind ny = ny / nl
    bind nz = nz / nl
    bind lx = 0.0 - 0.42
    bind ly = 0.0 - 0.78
    bind lz = 0.0 - 0.46
    bind ndl = abs(nx * lx + ny * ly + nz * lz)
    bind amb = sc_get("amb", 0.25)
    bind k = amb + (1.0 - amb) * ndl
    bind col = c_rgb(c_r(c) * k, c_g(c) * k, c_b(c) * k)
    bind d = (list_get(p0, 2) + list_get(p1, 2) + list_get(p2, 2) + list_get(p3, 2)) * 0.25
    bind rec = num_str(list_get(p0, 0)) + "," + num_str(list_get(p0, 1)) + "," + num_str(list_get(p1, 0)) + "," + num_str(list_get(p1, 1)) + "," + num_str(list_get(p2, 0)) + "," + num_str(list_get(p2, 1)) + "," + num_str(list_get(p3, 0)) + "," + num_str(list_get(p3, 1)) + "," + num_str(col) + "," + num_str(a)
    # keep the list sorted by depth (farthest first) as we insert
    bind n = st_llen("sf_d")
    bind pos = n
    while pos > 0.0 && st_lget("sf_d", pos - 1.0) < d { bind pos = pos - 1.0 }
    sl_insert("sf_d", pos, d)
    sl_insert("sf_rec", pos, rec)
    return 0.0
}
fn sw_solid_flush() {
    bind n = st_llen("sf_rec")
    bind i = 0.0
    while i < n {
        bind f = nums_parse(st_lget("sf_rec", i))
        bind c = list_get(f, 8)
        bind a = list_get(f, 9)
        g_tri_a(list_get(f, 0), list_get(f, 1), list_get(f, 2), list_get(f, 3), list_get(f, 4), list_get(f, 5), c, a)
        g_tri_a(list_get(f, 0), list_get(f, 1), list_get(f, 4), list_get(f, 5), list_get(f, 6), list_get(f, 7), c, a)
        bind i = i + 1.0
    }
    st_lclear("sf_d")
    st_lclear("sf_rec")
}
# the six faces of a box
fn sw_box_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    bind p0 = r3_local(0.0 - 1.0, 0.0 - 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p1 = r3_local(1.0, 0.0 - 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p2 = r3_local(1.0, 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p3 = r3_local(0.0 - 1.0, 1.0, 0.0 - 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p4 = r3_local(0.0 - 1.0, 0.0 - 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p5 = r3_local(1.0, 0.0 - 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p6 = r3_local(1.0, 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind p7 = r3_local(0.0 - 1.0, 1.0, 1.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    sw_quad(p0, p1, p2, p3, c, a)
    sw_quad(p5, p4, p7, p6, c, a)
    sw_quad(p4, p0, p3, p7, c, a)
    sw_quad(p1, p5, p6, p2, c, a)
    sw_quad(p4, p5, p1, p0, c, a)
    sw_quad(p3, p2, p6, p7, c, a)
}
fn sw_quad(p, q, r, s, c, a) {
    sw_face(list_get(p, 0), list_get(p, 1), list_get(p, 2), list_get(q, 0), list_get(q, 1), list_get(q, 2), list_get(r, 0), list_get(r, 1), list_get(r, 2), list_get(s, 0), list_get(s, 1), list_get(s, 2), c, a)
}
fn sw_plane_solid(cx, cy, cz, sx, sz, ry, c, a) {
    bind p0 = r3_local(0.0 - 1.0, 0.0, 0.0 - 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p1 = r3_local(1.0, 0.0, 0.0 - 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p2 = r3_local(1.0, 0.0, 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    bind p3 = r3_local(0.0 - 1.0, 0.0, 1.0, cx, cy, cz, sx, 1.0, sz, 0.0, ry, 0.0)
    sw_quad(p0, p1, p2, p3, c, a)
}
# lat/long sphere as quads (low segment count: it is a software renderer)
fn sw_sphere_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    bind rings = 6.0
    bind segs = 10.0
    bind i = 0.0
    while i < rings {
        bind t0 = i / rings * 3.14159265
        bind t1 = (i + 1.0) / rings * 3.14159265
        bind j = 0.0
        while j < segs {
            bind u0 = j / segs * 6.2831853
            bind u1 = (j + 1.0) / segs * 6.2831853
            bind p0 = r3_local(sin(t0) * cos(u0), 0.0 - cos(t0), sin(t0) * sin(u0), cx, cy, cz, sx, sy, sz, rx, ry, rz)
            bind p1 = r3_local(sin(t0) * cos(u1), 0.0 - cos(t0), sin(t0) * sin(u1), cx, cy, cz, sx, sy, sz, rx, ry, rz)
            bind p2 = r3_local(sin(t1) * cos(u1), 0.0 - cos(t1), sin(t1) * sin(u1), cx, cy, cz, sx, sy, sz, rx, ry, rz)
            bind p3 = r3_local(sin(t1) * cos(u0), 0.0 - cos(t1), sin(t1) * sin(u0), cx, cy, cz, sx, sy, sz, rx, ry, rz)
            sw_quad(p0, p1, p2, p3, c, a)
            bind j = j + 1.0
        }
        bind i = i + 1.0
    }
}
fn sw_cylinder_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    bind segs = 12.0
    bind j = 0.0
    while j < segs {
        bind u0 = j / segs * 6.2831853
        bind u1 = (j + 1.0) / segs * 6.2831853
        bind p0 = r3_local(cos(u0), 0.0 - 1.0, sin(u0), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        bind p1 = r3_local(cos(u1), 0.0 - 1.0, sin(u1), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        bind p2 = r3_local(cos(u1), 1.0, sin(u1), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        bind p3 = r3_local(cos(u0), 1.0, sin(u0), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        sw_quad(p0, p1, p2, p3, c, a)
        bind top = r3_local(0.0, 0.0 - 1.0, 0.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
        bind bot = r3_local(0.0, 1.0, 0.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
        sw_quad(top, p1, p0, top, c, a)
        sw_quad(bot, p3, p2, bot, c, a)
        bind j = j + 1.0
    }
}
fn sw_cone_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) {
    bind segs = 12.0
    bind apex = r3_local(0.0, 0.0 - 1.0, 0.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind bot = r3_local(0.0, 1.0, 0.0, cx, cy, cz, sx, sy, sz, rx, ry, rz)
    bind j = 0.0
    while j < segs {
        bind u0 = j / segs * 6.2831853
        bind u1 = (j + 1.0) / segs * 6.2831853
        bind p0 = r3_local(cos(u0), 1.0, sin(u0), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        bind p1 = r3_local(cos(u1), 1.0, sin(u1), cx, cy, cz, sx, sy, sz, rx, ry, rz)
        sw_quad(apex, p1, p0, apex, c, a)
        sw_quad(bot, p0, p1, bot, c, a)
        bind j = j + 1.0
    }
}

# wire or solid primitive by kind
fn sw_shape(kind, cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a, solid) {
    if solid {
        if kind == "box" || kind == "trigger" { sw_box_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
        else if kind == "sphere" || kind == "capsule" || kind == "torus" { sw_sphere_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
        else if kind == "cylinder" { sw_cylinder_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
        else if kind == "cone" { sw_cone_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
        else if kind == "plane" { sw_plane_solid(cx, cy, cz, sx, sz, ry, c, a) }
        else { sw_box_solid(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
        return 0.0
    }
    if kind == "box" || kind == "trigger" { sw_box(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "sphere" { sw_sphere(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "cylinder" { sw_cylinder(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "cone" { sw_cone(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "capsule" { sw_capsule(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "torus" { sw_torus(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    else if kind == "plane" { sw_plane(cx, cy, cz, sx, sz, ry, c, a) }
    else { sw_box(cx, cy, cz, sx, sy, sz, rx, ry, rz, c, a) }
    return 0.0
}

← Back to index