# ============================================================================ # camera.ludic — a first-person fly camera (yaw / pitch, metres) and its view / # projection matrices. Float bits throughout; see fmath.ludic. # ============================================================================ # the view frustum's four side planes (a, b, c, d), float bits: left, right, bottom, top; # from the clean (unjittered) view-projection, column-major m[col * 4 + row] function cam_init(render3d_st: mut Render3dState, aspect: float) -> void { render3d_st.cam_pos = v3_new(0.0, 2.0, 0.0) render3d_st.cam_view = m4_new(); render3d_st.cam_proj = m4_new(); render3d_st.cam_vp = m4_new(); render3d_st.cam_inv_vp = m4_new(); render3d_st.cam_inv_proj = m4_new() render3d_st.cam_vp_clean = m4_new(); render3d_st.cam_inv_vp_clean = m4_new() render3d_st.cam_fwd = v3_new(0.0, 0.0, -1.0) render3d_st.cam_right = v3_new(1.0, 0.0, 0.0) render3d_st.cam_fov = Math.deg_to_rad(42.0) render3d_st.cam_near = 0.3 render3d_st.cam_far = 14000.0 render3d_st.cam_aspect = aspect cam_update(render3d_st) } function cam_begin_frame(render3d_st: mut Render3dState, n: int, w: int, h: int) -> void { gsl_jitter_frame(render3d_st) cam_update(render3d_st) } function cam_set(render3d_st: mut Render3dState, x: float, y: float, z: float, yaw_deg: float, pitch_deg: float) -> void { v3_set(render3d_st.cam_pos, x, y, z) render3d_st.cam_yaw = Math.deg_to_rad(yaw_deg) render3d_st.cam_pitch = Math.deg_to_rad(pitch_deg) cam_update(render3d_st) } function cam_update(render3d_st: mut Render3dState) -> void { let cy = Math.cos(render3d_st.cam_yaw); let sy = Math.sin(render3d_st.cam_yaw) let cp = Math.cos(render3d_st.cam_pitch); let sp = Math.sin(render3d_st.cam_pitch) v3_set(render3d_st.cam_fwd, -(sy * cp), sp, -(cy * cp)) v3_set(render3d_st.cam_right, cy, 0.0, -sy) let px = render3d_st.cam_pos[0] let py = render3d_st.cam_pos[1] let pz = render3d_st.cam_pos[2] m4_look_at_xyz(render3d_st.cam_view, px, py, pz, px + render3d_st.cam_fwd[0], py + render3d_st.cam_fwd[1], pz + render3d_st.cam_fwd[2], 0.0, 1.0, 0.0) m4_perspective(render3d_st.cam_proj, render3d_st.cam_fov, render3d_st.cam_aspect, render3d_st.cam_near, r3d_reach(render3d_st, render3d_st.cam_far)) m4_mul(render3d_st.cam_vp_clean, render3d_st.cam_proj, render3d_st.cam_view) m4_inverse(render3d_st.cam_inv_vp_clean, render3d_st.cam_vp_clean) # DLSS's sub-pixel jitter (streamline.ludic), in the projection the scene draws with only: # culling, depth reconstruction and last frame's matrix keep the clean one if render3d_st.gsl_jitter_x != 0.0 or render3d_st.gsl_jitter_y != 0.0 { render3d_st.cam_proj[8] = render3d_st.cam_proj[8] - render3d_st.gsl_jitter_x render3d_st.cam_proj[9] = render3d_st.cam_proj[9] - render3d_st.gsl_jitter_y } m4_mul(render3d_st.cam_vp, render3d_st.cam_proj, render3d_st.cam_view) m4_inverse(render3d_st.cam_inv_vp, render3d_st.cam_vp) m4_inverse(render3d_st.cam_inv_proj, render3d_st.cam_proj) cam_planes_update(render3d_st) } function cam_planes_update(render3d_st: mut Render3dState) -> void { let m = render3d_st.cam_vp_clean for p in 0 .. 4 { var r = 0 if p >= 2 { r = 1 } var sg = 1.0 if p == 1 or p == 3 { sg = -1.0 } var a = m[3] + sg * m[r] var b = m[7] + sg * m[4 + r] var c = m[11] + sg * m[8 + r] var d = m[15] + sg * m[12 + r] let inv = 1.0 / Math.sqrt(a * a + b * b + c * c) render3d_st.cam_planes[p * 4] = a * inv; render3d_st.cam_planes[p * 4 + 1] = b * inv render3d_st.cam_planes[p * 4 + 2] = c * inv; render3d_st.cam_planes[p * 4 + 3] = d * inv } } # is a sphere (float bits) at least partly inside the side planes of the view? function cam_sphere_visible(render3d_st: Render3dState, x: float, y: float, z: float, r: float) -> bool { if render3d_st.cam_planes == null { return true } if r3d_beyond_fog(render3d_st, x, y, z, r) { return false } let nr = -r for p in 0 .. 4 { let o = p * 4 let dist = render3d_st.cam_planes[o] * x + render3d_st.cam_planes[o + 1] * y + render3d_st.cam_planes[o + 2] * z + render3d_st.cam_planes[o + 3] if dist < nr { return false } } return true } # is a sphere wholly past the fog wall? Nothing there is seen, so nothing there is drawn or animated export function r3d_beyond_fog(render3d_st: Render3dState, x: float, y: float, z: float, r: float) -> bool { let w = render3d_st.r3d_fog_wall if w <= 0.0 { return false } let dx = x - render3d_st.cam_pos[0]; let dy = y - render3d_st.cam_pos[1]; let dz = z - render3d_st.cam_pos[2] let far = w + R3D_FOG_MARGIN + r return dx * dx + dy * dy + dz * dz > far * far } # fly: forward/strafe in metres, turn in radians function cam_move(render3d_st: mut Render3dState, fwd: float, strafe: float, up: float, dyaw: float, dpitch: float) -> void { render3d_st.cam_yaw = render3d_st.cam_yaw + dyaw render3d_st.cam_pitch = Math.clamp(render3d_st.cam_pitch + dpitch, -1.5, 1.5) v3_madd(render3d_st.cam_pos, render3d_st.cam_pos, render3d_st.cam_fwd, fwd) v3_madd(render3d_st.cam_pos, render3d_st.cam_pos, render3d_st.cam_right, strafe) render3d_st.cam_pos[1] = render3d_st.cam_pos[1] + up cam_update(render3d_st) }