import Roact from "@rbxts/roact"
import { useEffect, useCallback, useMemo, useMutable } from "@rbxts/roact-hooked"
import { acrylicInstance } from "./acrylicInstance"
import { Lighting, Workspace } from "@rbxts/services"
import Make from "@rbxts/make"
const cylinderAngleOffset = CFrame.Angles(0, math.rad(90), 0)
function viewportPointToWorld(location: Vector2, distance: number): Vector3 {
const unitRay = Workspace.CurrentCamera!.ScreenPointToRay(location.X, location.Y)
return unitRay.Origin.add(unitRay.Direction.mul(distance))
}
function map(n: number, min0: number, max0: number, min1: number, max1: number): number {
return min1 + ((n - min0) * (max1 - min1)) / (max0 - min0)
}
function getOffset(): number {
return map(Workspace.CurrentCamera!.ViewportSize.Y, 0, 2560, 8, 56)
}
interface acrylicProps extends Roact.JsxInstanceProperties {
radius: number
distance: number
Event?: Roact.JsxInstanceEvents
Change?: Roact.JsxInstanceChangeEvents
}
Make("DepthOfFieldEffect", {
FarIntensity: 0,
InFocusRadius: 0.1,
NearIntensity: 1,
Parent: Lighting
})
function acrylic(props: acrylicProps): Roact.Element {
const { radius, distance } = props
const spreadableProps = { ...props } as Partial
delete spreadableProps.radius
delete spreadableProps.distance
const frameInfo = useMutable({
topleft2d: new Vector2(),
topright2d: new Vector2(),
bottomright2d: new Vector2(),
topleftradius2d: new Vector2()
})
const acrylic = useMemo(() => {
const clone = acrylicInstance.Clone()
clone.Parent = Workspace
return clone
}, [])
useEffect(() => {
return () => acrylic.Destroy()
}, [])
const updateFrameInfo = useCallback(
(size: Vector2, position: Vector2) => {
const topleftRaw = position.sub(size.div(2))
const info = frameInfo.current
info.topleft2d = new Vector2(math.ceil(topleftRaw.X), math.ceil(topleftRaw.Y))
info.topright2d = info.topleft2d.add(new Vector2(size.X, 0))
info.bottomright2d = info.topleft2d.add(size)
info.topleftradius2d = info.topleft2d.add(new Vector2(radius, 0))
},
[distance, radius]
)
const updateInstance = useCallback(() => {
const { topleft2d, topright2d, bottomright2d, topleftradius2d } = frameInfo.current
const topleft = viewportPointToWorld(topleft2d, distance)
const topright = viewportPointToWorld(topright2d, distance)
const bottomright = viewportPointToWorld(bottomright2d, distance)
const topleftradius = viewportPointToWorld(topleftradius2d, distance)
const cornerRadius = topleftradius.sub(topleft).Magnitude
const width = topright.sub(topleft).Magnitude
const height = topright.sub(bottomright).Magnitude
const center = CFrame.fromMatrix(
topleft.add(bottomright).div(2),
Workspace.CurrentCamera!.CFrame.XVector,
Workspace.CurrentCamera!.CFrame.YVector,
Workspace.CurrentCamera!.CFrame.ZVector
)
if (radius !== undefined && radius > 0) {
acrylic.Horizontal.CFrame = center
acrylic.Horizontal.Mesh.Scale = new Vector3(width - cornerRadius * 2, height, 0)
acrylic.Vertical.CFrame = center
acrylic.Vertical.Mesh.Scale = new Vector3(width, height - cornerRadius * 2, 0)
} else {
acrylic.Horizontal.CFrame = center
acrylic.Horizontal.Mesh.Scale = new Vector3(width, height, 0)
}
if (radius !== undefined && radius > 0) {
acrylic.TopLeft.CFrame = center
.mul(new CFrame(-width / 2 + cornerRadius, height / 2 - cornerRadius, 0))
.mul(cylinderAngleOffset)
acrylic.TopLeft.Mesh.Scale = new Vector3(0, cornerRadius * 2, cornerRadius * 2)
acrylic.TopRight.CFrame = center
.mul(new CFrame(width / 2 - cornerRadius, height / 2 - cornerRadius, 0))
.mul(cylinderAngleOffset)
acrylic.TopRight.Mesh.Scale = new Vector3(0, cornerRadius * 2, cornerRadius * 2)
acrylic.BottomLeft.CFrame = center
.mul(new CFrame(-width / 2 + cornerRadius, -height / 2 + cornerRadius, 0))
.mul(cylinderAngleOffset)
acrylic.BottomLeft.Mesh.Scale = new Vector3(0, cornerRadius * 2, cornerRadius * 2)
acrylic.BottomRight.CFrame = center
.mul(new CFrame(width / 2 - cornerRadius, -height / 2 + cornerRadius, 0))
.mul(cylinderAngleOffset)
acrylic.BottomRight.Mesh.Scale = new Vector3(0, cornerRadius * 2, cornerRadius * 2)
}
}, [radius, distance])
useEffect(() => {
updateInstance()
const posHandle = Workspace.CurrentCamera!.GetPropertyChangedSignal("CFrame").Connect(updateInstance)
const fovHandle = Workspace.CurrentCamera!.GetPropertyChangedSignal("FieldOfView").Connect(updateInstance)
const viewportHandle = Workspace.CurrentCamera!.GetPropertyChangedSignal("ViewportSize").Connect(updateInstance)
return () => {
posHandle.Disconnect()
fovHandle.Disconnect()
viewportHandle.Disconnect()
}
}, [updateInstance])
return (
{
const blurOffset = getOffset()
const size = rbx.AbsoluteSize.sub(new Vector2(blurOffset, blurOffset))
const position = rbx.AbsolutePosition.add(rbx.AbsoluteSize.div(2))
updateFrameInfo(size, position)
task.spawn(updateInstance)
},
AbsolutePosition: (rbx): void => {
const blurOffset = getOffset()
const size = rbx.AbsoluteSize.sub(new Vector2(blurOffset, blurOffset))
const position = rbx.AbsolutePosition.add(rbx.AbsoluteSize.div(2))
updateFrameInfo(size, position)
task.spawn(updateInstance)
}
}}
Size={new UDim2(1, 0, 1, 0)}
BackgroundTransparency={1}
/>
)
}
export default acrylic