164 lines
No EOL
5.4 KiB
TypeScript
164 lines
No EOL
5.4 KiB
TypeScript
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<Frame> {
|
|
radius: number
|
|
distance: number
|
|
|
|
Event?: Roact.JsxInstanceEvents<Frame>
|
|
Change?: Roact.JsxInstanceChangeEvents<Frame>
|
|
}
|
|
|
|
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<acrylicProps>
|
|
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 (
|
|
<frame
|
|
Change={{
|
|
AbsoluteSize: (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)
|
|
},
|
|
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 |