灵符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_cam.ling
# rt_cam.ling - the camera and the projection the engine uses, written out so the editor viewport
# and LingOS builds (no native 3-D) project points exactly like the native renderer:
#   v = p - t;  a = Ry(v);  b = Rx(a);  sx = cx + f*bx/(bz+zdist);  sy = cy + f*by/(bz+zdist)
# where Ry uses (cos yaw, sin yaw), Rx uses (cos pitch, sin pitch), f = viewport height.
# +y is down (as in the renderer). Camera state lives in the store under cam_*.

fn cam_set(x, y, z, yaw, pitch, zdist) {
    st_set("cam_x", x)
    st_set("cam_y", y)
    st_set("cam_z", z)
    st_set("cam_yaw", yaw)
    st_set("cam_pitch", pitch)
    st_set("cam_zd", max(0.2, zdist))
    st_set("cam_cy", cos(yaw))
    st_set("cam_sy", sin(yaw))
    st_set("cam_cp", cos(pitch))
    st_set("cam_sp", sin(pitch))
}
# where on screen the camera draws: a viewport rectangle
fn cam_viewport(x, y, w, h) {
    st_set("vp_x", x)
    st_set("vp_y", y)
    st_set("vp_w", w)
    st_set("vp_h", h)
    st_set("vp_cx", x + w * 0.5)
    st_set("vp_cy", y + h * 0.5)
    st_set("vp_f", max(1.0, h))
}
fn cam_x() { return st_get("cam_x", 0.0) }
fn cam_y() { return st_get("cam_y", 0.0) }
fn cam_z() { return st_get("cam_z", 0.0) }
fn cam_yaw() { return st_get("cam_yaw", 0.0) }
fn cam_pitch() { return st_get("cam_pitch", 0.0) }
fn cam_zd() { return st_get("cam_zd", 2.0) }

# camera-space point [bx, by, bz]
fn cam_to_view(x, y, z) {
    bind vx = x - st_get("cam_x", 0.0)
    bind vy = y - st_get("cam_y", 0.0)
    bind vz = z - st_get("cam_z", 0.0)
    bind cy = st_get("cam_cy", 1.0)
    bind sy = st_get("cam_sy", 0.0)
    bind cp = st_get("cam_cp", 1.0)
    bind sp = st_get("cam_sp", 0.0)
    bind ax = cy * vx - sy * vz
    bind ay = vy
    bind az = sy * vx + cy * vz
    bind bx = ax
    bind by = cp * ay - sp * az
    bind bz = sp * ay + cp * az
    return [bx, by, bz]
}
# screen point [sx, sy, depth]; depth <= -zdist + 0.05 means behind the eye
fn cam_project(x, y, z) {
    bind b = cam_to_view(x, y, z)
    bind bz = list_get(b, 2)
    bind d = bz + st_get("cam_zd", 2.0)
    if d < 0.05 { bind d = 0.05 }
    bind f = st_get("vp_f", 600.0)
    return [st_get("vp_cx", 400.0) + f * list_get(b, 0) / d, st_get("vp_cy", 300.0) + f * list_get(b, 1) / d, bz]
}
fn cam_visible(depth) { return depth > 0.05 - st_get("cam_zd", 2.0) }
# pixels per world unit at a depth (gizmo sizing)
fn cam_scale_at(depth) {
    bind d = depth + st_get("cam_zd", 2.0)
    if d < 0.05 { bind d = 0.05 }
    return st_get("vp_f", 600.0) / d
}

# camera basis vectors in world space (right, up, forward)
fn cam_right() {
    bind cy = st_get("cam_cy", 1.0)
    bind sy = st_get("cam_sy", 0.0)
    return [cy, 0.0, 0.0 - sy]
}
fn cam_forward() {
    bind cy = st_get("cam_cy", 1.0)
    bind sy = st_get("cam_sy", 0.0)
    bind cp = st_get("cam_cp", 1.0)
    bind sp = st_get("cam_sp", 0.0)
    return [sy * cp, sp, cy * cp]
}
fn cam_up() {
    bind cy = st_get("cam_cy", 1.0)
    bind sy = st_get("cam_sy", 0.0)
    bind cp = st_get("cam_cp", 1.0)
    bind sp = st_get("cam_sp", 0.0)
    return [sy * sp, 0.0 - cp, cy * sp]
}

# a ray from the eye through a screen point: [ox,oy,oz, dx,dy,dz]
fn cam_ray(sx, sy) {
    bind f = st_get("vp_f", 600.0)
    bind dcx = (sx - st_get("vp_cx", 400.0)) / f
    bind dcy = (sy - st_get("vp_cy", 300.0)) / f
    bind cy = st_get("cam_cy", 1.0)
    bind sy2 = st_get("cam_sy", 0.0)
    bind cp = st_get("cam_cp", 1.0)
    bind sp = st_get("cam_sp", 0.0)
    bind ax = dcx
    bind ay = cp * dcy + sp
    bind az = 0.0 - sp * dcy + cp
    bind dx = cy * ax + sy2 * az
    bind dy = ay
    bind dz = 0.0 - sy2 * ax + cy * az
    bind ln = sqrt(dx * dx + dy * dy + dz * dz)
    if ln < 0.000001 { bind ln = 1.0 }
    return [st_get("cam_x", 0.0), st_get("cam_y", 0.0), st_get("cam_z", 0.0), dx / ln, dy / ln, dz / ln]
}
# where that ray meets the horizontal plane y = py, or [] when parallel / behind
fn cam_ray_plane_y(ray, py) {
    bind dy = list_get(ray, 4)
    if abs(dy) < 0.00001 { return [] }
    bind t = (py - list_get(ray, 1)) / dy
    if t < 0.0 { return [] }
    return [list_get(ray, 0) + list_get(ray, 3) * t, py, list_get(ray, 2) + list_get(ray, 5) * t]
}

# orbit camera: looks at a target from a distance along yaw / pitch
fn cam_orbit(tx, ty, tz, yaw, pitch, dist, zdist) {
    bind cp = cos(pitch)
    bind sp = sin(pitch)
    bind fx = sin(yaw) * cp
    bind fy = sp
    bind fz = cos(yaw) * cp
    cam_set(tx - fx * dist, ty - fy * dist, tz - fz * dist, yaw, pitch, zdist)
}
# look-at: yaw / pitch that aim from (x,y,z) at (tx,ty,tz)
fn cam_look_at(x, y, z, tx, ty, tz, zdist) {
    bind dx = tx - x
    bind dy = ty - y
    bind dz = tz - z
    bind yaw = atan2(dx, dz)
    bind horiz = sqrt(dx * dx + dz * dz)
    bind pitch = atan2(dy, horiz)
    cam_set(x, y, z, yaw, pitch, zdist)
}

# push the camera to the native renderer (desktop / web builds)
fn cam_apply_native() {
    if !plat_has_3d() { return 0.0 }
    set_camera(st_get("cam_cy", 1.0), st_get("cam_sy", 0.0), st_get("cam_cp", 1.0), st_get("cam_sp", 0.0))
    set_camera_pos(st_get("cam_x", 0.0), st_get("cam_y", 0.0), st_get("cam_z", 0.0))
    set_zdist(st_get("cam_zd", 2.0))
    return 0.0
}

← Back to index