Skip to content

Instantly share code, notes, and snippets.

@MrChickenRocket
Last active April 22, 2024 09:51
Show Gist options
  • Save MrChickenRocket/09050693faee256888313c04d93cbb08 to your computer and use it in GitHub Desktop.
Save MrChickenRocket/09050693faee256888313c04d93cbb08 to your computer and use it in GitHub Desktop.

Revisions

  1. MrChickenRocket revised this gist Feb 9, 2023. 1 changed file with 36 additions and 72 deletions.
    108 changes: 36 additions & 72 deletions CameraFrustum.lua
    Original file line number Diff line number Diff line change
    @@ -1,91 +1,55 @@
    --Ultra simple module that lets you generate a camera frustum in roblox and test it for spheres and aabbs
    local Frustum = {}

    local module = {}
    local function planeFromPoints(p0, p1, p2)
    local normal = (p1 - p0):Cross(p2 - p1).Unit

    local function PlaneFromPoints(v0,v1,v2)

    local xx = v1 - v0
    local yy = v2 - v0
    local normal = xx:Cross(yy)
    normal = normal.Unit

    local res = {
    n = normal,
    d = -v0:Dot(normal)
    return {
    normal = normal,
    d = -normal:Dot(p0),
    }

    return res
    end

    function module:IsSphereVisible(planes, worldPoint, radius)

    for j = 1, 6 do
    local dist = worldPoint:Dot(planes[j].n) + planes[j].d + radius
    if(dist < 0) then
    return false
    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)

    --Not really tested this, think its correct :D
    function module:IsAABBVisible(planes, aabbMin, aabbMax)
    local result = "INSIDE"

    for i = 1, #planes do
    local plane = planes[i]
    local nx = plane.normal.x > 0
    local ny = plane.normal.y > 0
    local nz = plane.normal.z > 0

    local dot = plane.normal:Dot(Vector3:new{x = (nx == 1 and aabbMax.x or aabbMin.x), y = (ny == 1 and aabbMax.y or aabbMin.y), z = (nz == 1 and aabbMax.z or aabbMin.z)})

    if dot < -plane.offset then
    return "OUTSIDE"
    end

    local dot2 = plane.normal:Dot(Vector3:new{x = (nx == 0 and aabbMax.x or aabbMin.x), y = (ny == 0 and aabbMax.y or aabbMin.y), z = (nz == 0 and aabbMax.z or aabbMin.z)})

    if dot2 <= -plane.offset then
    result = "INTERSECTS"
    end
    end

    return result
    end
    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

    function module:MakePlanes(camera, farDist)

    local x = camera.ViewportSize.X
    local y = camera.ViewportSize.Y

    local a1 = camera:ViewportPointToRay(0, 0, 1)
    local c1 = camera:ViewportPointToRay(x, 0, 1)
    local a2 = camera:ViewportPointToRay(0, y, 1)
    local c2 = camera:ViewportPointToRay(x, y, 1)
    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 ra1 = a1.Origin + a1.Direction
    local ra2 = a2.Origin + a2.Direction
    local rb1 = a1.Origin + a1.Direction * farDist
    local rb2 = a2.Origin + a2.Direction * farDist
    local rc1 = c1.Origin + c1.Direction
    local rc2 = c2.Origin + c2.Direction
    local rd1 = c1.Origin + c1.Direction * farDist
    local rd2 = c2.Origin + c2.Direction * farDist
    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)

    --build some planes
    local planes = {}
    planes[1] = PlaneFromPoints(ra1, rc1, ra2) --near
    planes[2] = PlaneFromPoints(rb1, rb2,rd1) --far - do we need a far plane?
    local frustum = {}

    planes[3] = PlaneFromPoints(ra1,rb1,rc1) --top
    planes[4] = PlaneFromPoints(ra1,ra2,rb1) --left Is this correct??
    planes[5] = PlaneFromPoints(ra2,rc2,rb2) --bot
    planes[6] = PlaneFromPoints(rc1,rd1,rd2) --right
    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 planes
    return frustum
    end

    return module
    return Frustum
  2. MrChickenRocket revised this gist Feb 8, 2023. 1 changed file with 25 additions and 16 deletions.
    41 changes: 25 additions & 16 deletions CameraFrustum.lua
    Original file line number Diff line number Diff line change
    @@ -30,21 +30,30 @@ function module:IsSphereVisible(planes, worldPoint, radius)
    end


    function module:IsAABBVisible(planes, min, max)

    for j = 1, 6 do
    local d = planes[j].n:Dot(min) + planes[j].d
    if (d < 0) then
    return false
    end

    d = planes[j].n:Dot(max) + planes[j].d
    if (d < 0) then
    return false
    end
    end

    return true
    --Not really tested this, think its correct :D
    function module:IsAABBVisible(planes, aabbMin, aabbMax)
    local result = "INSIDE"

    for i = 1, #planes do
    local plane = planes[i]
    local nx = plane.normal.x > 0
    local ny = plane.normal.y > 0
    local nz = plane.normal.z > 0

    local dot = plane.normal:Dot(Vector3:new{x = (nx == 1 and aabbMax.x or aabbMin.x), y = (ny == 1 and aabbMax.y or aabbMin.y), z = (nz == 1 and aabbMax.z or aabbMin.z)})

    if dot < -plane.offset then
    return "OUTSIDE"
    end

    local dot2 = plane.normal:Dot(Vector3:new{x = (nx == 0 and aabbMax.x or aabbMin.x), y = (ny == 0 and aabbMax.y or aabbMin.y), z = (nz == 0 and aabbMax.z or aabbMin.z)})

    if dot2 <= -plane.offset then
    result = "INTERSECTS"
    end
    end

    return result
    end

    function module:MakePlanes(camera, farDist)
    @@ -72,7 +81,7 @@ function module:MakePlanes(camera, farDist)
    planes[2] = PlaneFromPoints(rb1, rb2,rd1) --far - do we need a far plane?

    planes[3] = PlaneFromPoints(ra1,rb1,rc1) --top
    planes[4] = PlaneFromPoints(ra1,ra2,rb1) --left
    planes[4] = PlaneFromPoints(ra1,ra2,rb1) --left Is this correct??
    planes[5] = PlaneFromPoints(ra2,rc2,rb2) --bot
    planes[6] = PlaneFromPoints(rc1,rd1,rd2) --right

  3. MrChickenRocket created this gist Feb 8, 2023.
    82 changes: 82 additions & 0 deletions CameraFrustum.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    --Ultra simple module that lets you generate a camera frustum in roblox and test it for spheres and aabbs

    local module = {}

    local function PlaneFromPoints(v0,v1,v2)

    local xx = v1 - v0
    local yy = v2 - v0
    local normal = xx:Cross(yy)
    normal = normal.Unit

    local res = {
    n = normal,
    d = -v0:Dot(normal)
    }

    return res
    end

    function module:IsSphereVisible(planes, worldPoint, radius)

    for j = 1, 6 do
    local dist = worldPoint:Dot(planes[j].n) + planes[j].d + radius
    if(dist < 0) then
    return false
    end
    end

    return true
    end


    function module:IsAABBVisible(planes, min, max)

    for j = 1, 6 do
    local d = planes[j].n:Dot(min) + planes[j].d
    if (d < 0) then
    return false
    end

    d = planes[j].n:Dot(max) + planes[j].d
    if (d < 0) then
    return false
    end
    end

    return true
    end

    function module:MakePlanes(camera, farDist)

    local x = camera.ViewportSize.X
    local y = camera.ViewportSize.Y

    local a1 = camera:ViewportPointToRay(0, 0, 1)
    local c1 = camera:ViewportPointToRay(x, 0, 1)
    local a2 = camera:ViewportPointToRay(0, y, 1)
    local c2 = camera:ViewportPointToRay(x, y, 1)

    local ra1 = a1.Origin + a1.Direction
    local ra2 = a2.Origin + a2.Direction
    local rb1 = a1.Origin + a1.Direction * farDist
    local rb2 = a2.Origin + a2.Direction * farDist
    local rc1 = c1.Origin + c1.Direction
    local rc2 = c2.Origin + c2.Direction
    local rd1 = c1.Origin + c1.Direction * farDist
    local rd2 = c2.Origin + c2.Direction * farDist

    --build some planes
    local planes = {}
    planes[1] = PlaneFromPoints(ra1, rc1, ra2) --near
    planes[2] = PlaneFromPoints(rb1, rb2,rd1) --far - do we need a far plane?

    planes[3] = PlaneFromPoints(ra1,rb1,rc1) --top
    planes[4] = PlaneFromPoints(ra1,ra2,rb1) --left
    planes[5] = PlaneFromPoints(ra2,rc2,rb2) --bot
    planes[6] = PlaneFromPoints(rc1,rd1,rd2) --right

    return planes
    end

    return module