local Frustum = {} local function planeFromPoints(p0, p1, p2) local normal = (p1 - p0):Cross(p2 - p1).Unit return { normal = normal, d = -normal:Dot(p0), } end function Frustum.sphereInFrustum(frustum, origin, radius) for _, plane in frustum do local distance = origin:Dot(plane.normal) + plane.d + radius if distance <= 0 then return false end end return true end function Frustum.fromCamera(camera, farPlaneZ) local aspectRatio = camera.ViewportSize.X / camera.ViewportSize.Y local halfFov = math.rad(camera.FieldOfView / 2) local halfFarPlaneHeight = 2 * math.tan(halfFov) * farPlaneZ / 2 local halfFarPlaneWidth = halfFarPlaneHeight * aspectRatio local halfNearPlaneHeight = 2 * math.tan(halfFov) * -camera.NearPlaneZ / 2 local halfNearPlaneWidth = halfNearPlaneHeight * aspectRatio local farTopLeft = camera.CFrame * Vector3.new(-halfFarPlaneWidth, halfFarPlaneHeight, -farPlaneZ) local farTopRight = camera.CFrame * Vector3.new(halfFarPlaneWidth, halfFarPlaneHeight, -farPlaneZ) local farBottomRight = camera.CFrame * Vector3.new(halfFarPlaneWidth, -halfFarPlaneHeight, -farPlaneZ) local nearTopLeft = camera.CFrame * Vector3.new(-halfNearPlaneWidth, halfNearPlaneHeight, camera.NearPlaneZ) local nearTopRight = camera.CFrame * Vector3.new(halfNearPlaneWidth, halfNearPlaneHeight, camera.NearPlaneZ) local nearBottomLeft = camera.CFrame * Vector3.new(-halfNearPlaneWidth, -halfNearPlaneHeight, camera.NearPlaneZ) local nearBottomRight = camera.CFrame * Vector3.new(halfNearPlaneWidth, -halfNearPlaneHeight, camera.NearPlaneZ) local frustum = {} frustum.near = planeFromPoints(nearTopRight, nearBottomRight, nearTopLeft) frustum.far = planeFromPoints(farTopRight, farTopLeft, farBottomRight) frustum.top = planeFromPoints(nearTopRight, nearTopLeft, farTopRight) frustum.bottom = planeFromPoints(nearBottomRight, farBottomRight, nearBottomLeft) frustum.left = planeFromPoints(nearTopLeft, nearBottomLeft, farTopLeft) frustum.right = planeFromPoints(nearTopRight, farTopRight, nearBottomRight) return frustum end return Frustum