lingdev
↓ 0 downloadsLingDev: 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).
Keywords game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdevCode / 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
}