VPFBind - Display Workspace in ViewportFrames, without headache!

Hello! Many times in some of my project I needed to display a region of Workspace in a ViewportFrame. However, this is not possible due to the limitations of ViewportFrames. All displayed Parts need to be descendant of ViewportFrame, so in order to display Parts from Workspace you either need to clone everything to your ViewportFrame if you need a static image, or… You must spend countless hours on getting Workspace descendants to replicate inside ViewportFrame only to end up with your script being massive, unreadable and slow.
Because of the problems listed above, I decided to make my own module for binding VPFs to Workspace. There’re somewhat similar modules out there, but they seem to be serving a different purpose, and I couldn’t find any alternatives so I wrote the module myself. It’s pretty simple and lightweight, I tried to optimize it but I think with a little bit of work it can be made even faster, even though I’m satisfied with the current performance. I decided not to upload it on marketplace and instead just make it a signle-file drop-in module:

Source code
--!strict
--!optimize 2
--!native

--[[
VPFBind v1.0.0 by @GulgPlayer

VPFBind allows you to display parts and models from Workspace in ViewportFrames with the maximum performance

Example usage:
	local VPFBind = require(script.VPFBind)

	local Camera = Instance.new("Camera")
	Camera.Parent = script.Parent
	script.Parent.CurrentCamera = Camera

	VPFBind(script.Parent, { Camera = workspace.CurrentCamera })
]]

local CollectionService = game:GetService("CollectionService")
local HttpService = game:GetService("HttpService")
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")

local FLIP_CFRAME = CFrame.new(0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1)
local SB_CFRAME = FLIP_CFRAME * CFrame.fromOrientation(math.pi, -math.pi / 2, 0)
local SBDN_CFRAME = FLIP_CFRAME * CFrame.fromOrientation(math.pi, math.pi / 2, 0)

local SKYBOX: { [string]: Enum.NormalId } = {
	SkyboxBk = Enum.NormalId.Right,
	SkyboxDn = Enum.NormalId.Bottom,
	SkyboxFt = Enum.NormalId.Left,
	SkyboxLf = Enum.NormalId.Back,
	SkyboxRt = Enum.NormalId.Front,
	SkyboxUp = Enum.NormalId.Top,
}

export type VPFBindOptions = {
	Camera: Camera?,
	RenderDistance: number?,
	WorldModel: ("WorldModel" | "Model")?,
	OverlapParams: OverlapParams?,
	GCInterval: number?,
	ReplicateHighPriority: { [string]: {string} }?,
	ReplicateLowPriority: { [string]: {string} }?,
	ReplicateChildren: {string}?,
	CharacterFPS: number?,
	HighPriorityFPS: number?,
	LowPriorityFPS: number?,
	Skybox: boolean?,
	SkyboxCelestialBodies: boolean?
}

local function replicateChild(vpfPart: Instance, part: Instance, replicateTypes: {string}, child: Instance, static: boolean?): ()
	local shouldReplicate = false
	for _, childType in replicateTypes do
		if child:IsA(childType) then
			shouldReplicate = true
			break
		end
	end
	if not shouldReplicate then return end
	
	local vpfChild = Instance.fromExisting(child)
	vpfChild.Parent = vpfPart
	if not static then
		child.Changed:Connect(function(prop)
			(vpfChild :: any)[prop] = (child :: any)[prop]
		end)
	end
	part.ChildRemoved:Connect(function(removedChild)
		if removedChild == child then
			vpfChild:Destroy()
		end
	end)
end

local ReplicateModelChildren = { "Clothing", "Humanoid" }

local function assign(to: { [string]: any }, from: { [string]: any }?): ()
	if from then
		for k, v in from do
			to[k] = v
		end
	end
end

-- Unbind VPF from the Workspace and perform clean-up
local function VPFUnbind(): () end

-- Bind VPF to render a chunk of Workspace with specified camera
local function VPFBind(vpf: ViewportFrame, vpfBindOptions: VPFBindOptions?): typeof(VPFUnbind)
	local opts: any = {
		Camera = nil,
		RenderDistance = 1024,
		WorldModel = "Model",
		OverlapParams = OverlapParams.new(),
		GCInterval = 30,
		ReplicateHighPriority = { BasePart={ "CFrame" } },
		ReplicateLowPriority = { BasePart={ "Transparency", "LocalTransparencyModifier", "Color", "Size" } },
		ReplicateChildren = { "Decal", "WrapTarget", "DataModelMesh" },
		CharacterFPS = 60,
		HighPriorityFPS = 60,
		LowPriorityFPS = 24,
		Skybox = true,
		SkyboxCelestialBodies = Lighting.Sky.CelestialBodiesShown
	}
	assign(opts, vpfBindOptions)
	
	local id = `VPF-{HttpService:GenerateGUID(false)}`
	
	local gcElapsedTime = 0
	
	local characterSyncElapsedTime = 0
	local highPrioritySyncElapsedTime = 0
	local lowPrioritySyncElapsedTime = 0
	
	local partsFolder = Instance.new(opts.WorldModel)
	partsFolder.Name = "Workspace"
	partsFolder.Parent = vpf
	
	local skyboxModel = Instance.new("Model")
	skyboxModel.Name = "Skybox"
	
	local vpfSky = Instance.fromExisting(Lighting.Sky)
	vpfSky.Parent = vpf

	local skybox = Instance.new("Part")
	skybox.Name = "Skybox"
	skybox.CFrame = SB_CFRAME
	skybox.Size = Vector3.one * (opts.RenderDistance * 2)
	skybox.Transparency = 1
	skybox.Parent = skyboxModel

	local skyboxDn = Instance.new("Part")
	skyboxDn.Name = "SkyboxDn"
	skyboxDn.CFrame = SBDN_CFRAME
	skyboxDn.Size = Vector3.one * (opts.RenderDistance * 2)
	skyboxDn.Transparency = 1
	skyboxDn.Parent = skyboxModel

	for side, face in SKYBOX do
		local decal = Instance.new("Decal")
		decal.Name = side;
		decal.Face = face
		decal.Texture = Lighting.Sky[side]
		decal.Parent = if side == "SkyboxDn" then skyboxDn else skybox
	end

	skyboxModel.Parent = vpf
	
	local renderSteppedConnection: RBXScriptConnection,
		  destroyConnection: RBXScriptConnection,
		  descendantRemovingConnection: RBXScriptConnection
	
	local connections: { [string]: {RBXScriptConnection} } = {}
	
	local function destroyVPFPart(vpfPart: any): ()
		for _, connection in connections[vpfPart:GetAttribute("Id")] do
			connection:Disconnect()
		end
		vpfPart.WorkspaceRef.Value:FindFirstChild(id):Destroy()
		vpfPart:Destroy()
	end
	
	local function createVPFPart(parent: Instance, part: BasePart): BasePart
		local vpfPart = Instance.fromExisting(part)
		vpfPart:SetAttribute("Id", HttpService:GenerateGUID())
		local vpf2workspaceRef = Instance.new("ObjectValue")
		vpf2workspaceRef.Name = "WorkspaceRef"
		vpf2workspaceRef.Value = part
		vpf2workspaceRef.Parent = vpfPart
		vpfPart.Parent = parent
		local workspace2vpfRef = Instance.new("ObjectValue")
		workspace2vpfRef.Archivable = false
		workspace2vpfRef.Name = id
		workspace2vpfRef.Value = vpfPart
		workspace2vpfRef.Parent = part
		return vpfPart
	end
	
	local function render(deltaTime: number): ()
		if not vpf.CurrentCamera then return end
		
		if opts.Camera then
			vpf.CurrentCamera.CFrame = opts.Camera.CFrame
		end
		
		local camera: Camera = opts.Camera or vpf.CurrentCamera
		skybox.Position = camera.CFrame.Position
		skyboxDn.Position = camera.CFrame.Position
		
		gcElapsedTime += deltaTime
		characterSyncElapsedTime += deltaTime
		highPrioritySyncElapsedTime += deltaTime
		lowPrioritySyncElapsedTime += deltaTime
		
		local syncCharacters = characterSyncElapsedTime >= 1 / opts.CharacterFPS
		local syncHighPriority = highPrioritySyncElapsedTime >= 1 / opts.HighPriorityFPS
		local syncLowPriority = lowPrioritySyncElapsedTime >= 1 / opts.LowPriorityFPS
		
		if not syncCharacters and not syncHighPriority and not syncLowPriority then
			return
		end
		
		if syncCharacters then characterSyncElapsedTime -= 1 / opts.CharacterFPS end
		if syncHighPriority then highPrioritySyncElapsedTime -= 1 / opts.HighPriorityFPS end
		if syncLowPriority then lowPrioritySyncElapsedTime -= 1 / opts.LowPriorityFPS end
		
		local shouldSync = { syncLowPriority, syncHighPriority, syncCharacters }
		
		local gc = gcElapsedTime >= opts.GCInterval
		if gc then gcElapsedTime -= opts.GCInterval end
		
		local parts = workspace:GetPartBoundsInRadius(camera.CFrame.Position, opts.RenderDistance, opts.OverlapParams)
		
		if gc then
			for _, vpfPart in partsFolder:GetChildren() do
				CollectionService:AddTag(vpfPart, `{id}-GC`)
			end
		end
		
		for _, part in parts do
			if not part:IsA("BasePart") then continue end
			
			local partParent: Instance = partsFolder
			
			local syncPriority = if CollectionService:HasTag(part, "VPF-HighPriority") then 2 else 1
			
			local model = part.Parent
			if model:IsA("Accessory") or model:IsA("Tool") then
				model = model.Parent
			end
			
			if model:FindFirstChildWhichIsA("Humanoid") then
				local saved: ObjectValue = model:FindFirstChild(id)
				if saved then
					partParent = saved.Value :: Instance
				else
					local vpfModel = createVPFPart(partParent, model)
					partParent = vpfModel
					for _, child in model:GetChildren() do
						replicateChild(vpfModel, model, ReplicateModelChildren, child, true)
					end
					model.ChildAdded:Connect(function(child)
						replicateChild(vpfModel, model, ReplicateModelChildren, child, true)
					end)
				end
				syncPriority = 3
			end
			
			if not shouldSync[syncPriority] then continue end
			
			local saved: ObjectValue = part:FindFirstChild(id)
			if saved then
				local vpfPart = saved.Value :: typeof(part)
				for partType, props in opts.ReplicateHighPriority do
					if part:IsA(partType) then
						for _, prop in props do
							vpfPart[prop] = part[prop]
						end
					end
				end
				CollectionService:RemoveTag(vpfPart, `{id}-GC`)
			else
				local vpfPart = createVPFPart(partParent, part)
				local partConnections = {}
				for partType, props in opts.ReplicateLowPriority do
					if part:IsA(partType) then
						for _, prop in props do
							partConnections[#partConnections + 1] = part:GetPropertyChangedSignal(prop):Connect(function()
								(vpfPart :: any)[prop] = part[prop]
							end)
						end
					end
				end
				connections[vpfPart:GetAttribute("Id")] = partConnections
				for _, child in part:GetChildren() do
					replicateChild(vpfPart, part, opts.ReplicateChildren, child)
				end
				part.ChildAdded:Connect(function(child)
					replicateChild(vpfPart, part, opts.ReplicateChildren, child)
				end)
			end
		end
		
		if gc then
			for _, vpfPart in CollectionService:GetTagged(`{id}-GC`) do
				if vpfPart:IsA("Model") then
					for _, child in vpfPart:GetChildren() do
						if vpfPart:IsA("BasePart") then
							destroyVPFPart(child)
						end
					end
				else
					destroyVPFPart(vpfPart)
				end
			end
		end
	end
	
	renderSteppedConnection = RunService.RenderStepped:Connect(render)
	
	descendantRemovingConnection = workspace.DescendantRemoving:Connect(function(descendant)
		local ref = descendant:FindFirstChild(id)
		if ref then
			destroyVPFPart(ref.Value)
		end
	end)
	
	local function destroy()
		renderSteppedConnection:Disconnect()
		destroyConnection:Disconnect()
		descendantRemovingConnection:Disconnect()
		for _, vpfPart in partsFolder:GetChildren() do
			(((vpfPart :: any).WorkspaceRef :: ObjectValue).Value :: BasePart):FindFirstChild(id):Destroy()
		end
		for _, partConnections in connections do
			for _, connection in partConnections do
				connection:Disconnect()
			end
		end
		partsFolder:Destroy()
		vpfSky:Destroy()
		skyboxModel:Destroy()
	end
	destroyConnection = vpf.Destroying:Connect(destroy)
	
	return destroy
end

return VPFBind

It’s free to use, and you don’t have to give me any credit, the best reward for my work will be just seeing people use it and I hope this module helps you.

Here’s a showcase of what can be achieved using VPFBind:
Demo video (4.3 MB)
Demo place

Code used in demo
local VPFBind = require(script.VPFBind)

local Camera = Instance.new("Camera")
Camera.Parent = script.Parent
script.Parent.CurrentCamera = Camera

VPFBind(script.Parent, { Camera = workspace.CurrentCamera, RenderDistance = 512 })

I actually developed it for my other project, which is a high-quality portal system. While being in proccess of making it, I realized that I don’t actually need this module, but because I have already done a part of work I wonder if anyone else wants me to release this in public. I made a post about this in #development-discussion, but it got removed for being off-topic.
So, what do you think? If you have any ideas or suggestions, feel free to comment! Hope you will find my module useful :slight_smile:

What should I do?
  • Finish your portal system, I would actually use it in my projects
  • Give up on your portal system, it would be less useful than VPFBind
  • VPFBind isn’t useful

0 voters

5 Likes

I changed the code to find Lighting:FindFirstChildOfClass(“Sky”) instead of Lighting.Sky:

--!strict
--!optimize 2
--!native

--[[
VPFBind v1.0.0 by @GulgPlayer

VPFBind allows you to display parts and models from Workspace in ViewportFrames with the maximum performance

Example usage:
	local VPFBind = require(script.VPFBind)

	local Camera = Instance.new("Camera")
	Camera.Parent = script.Parent
	script.Parent.CurrentCamera = Camera

	VPFBind(script.Parent, { Camera = workspace.CurrentCamera })
]]

local CollectionService = game:GetService("CollectionService")
local HttpService = game:GetService("HttpService")
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")

local FLIP_CFRAME = CFrame.new(0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1)
local SB_CFRAME = FLIP_CFRAME * CFrame.fromOrientation(math.pi, -math.pi / 2, 0)
local SBDN_CFRAME = FLIP_CFRAME * CFrame.fromOrientation(math.pi, math.pi / 2, 0)

local SKYBOX: { [string]: Enum.NormalId } = {
	SkyboxBk = Enum.NormalId.Right,
	SkyboxDn = Enum.NormalId.Bottom,
	SkyboxFt = Enum.NormalId.Left,
	SkyboxLf = Enum.NormalId.Back,
	SkyboxRt = Enum.NormalId.Front,
	SkyboxUp = Enum.NormalId.Top,
}

export type VPFBindOptions = {
	Camera: Camera?,
	RenderDistance: number?,
	WorldModel: ("WorldModel" | "Model")?,
	OverlapParams: OverlapParams?,
	GCInterval: number?,
	ReplicateHighPriority: { [string]: {string} }?,
	ReplicateLowPriority: { [string]: {string} }?,
	ReplicateChildren: {string}?,
	CharacterFPS: number?,
	HighPriorityFPS: number?,
	LowPriorityFPS: number?,
	Skybox: boolean?,
	SkyboxCelestialBodies: boolean?
}

local function replicateChild(vpfPart: Instance, part: Instance, replicateTypes: {string}, child: Instance, static: boolean?): ()
	local shouldReplicate = false
	for _, childType in replicateTypes do
		if child:IsA(childType) then
			shouldReplicate = true
			break
		end
	end
	if not shouldReplicate then return end

	local vpfChild = Instance.fromExisting(child)
	vpfChild.Parent = vpfPart
	if not static then
		child.Changed:Connect(function(prop)
			(vpfChild :: any)[prop] = (child :: any)[prop]
		end)
	end
	part.ChildRemoved:Connect(function(removedChild)
		if removedChild == child then
			vpfChild:Destroy()
		end
	end)
end

local ReplicateModelChildren = { "Clothing", "Humanoid" }

local function assign(to: { [string]: any }, from: { [string]: any }?): ()
	if from then
		for k, v in from do
			to[k] = v
		end
	end
end

-- Unbind VPF from the Workspace and perform clean-up
local function VPFUnbind(): () end

-- Bind VPF to render a chunk of Workspace with specified camera
local function VPFBind(vpf: ViewportFrame, vpfBindOptions: VPFBindOptions?): typeof(VPFUnbind)
	local opts: any = {
		Camera = nil,
		RenderDistance = 1024,
		WorldModel = "Model",
		OverlapParams = OverlapParams.new(),
		GCInterval = 30,
		ReplicateHighPriority = { BasePart={ "CFrame" } },
		ReplicateLowPriority = { BasePart={ "Transparency", "LocalTransparencyModifier", "Color", "Size" } },
		ReplicateChildren = { "Decal", "WrapTarget", "DataModelMesh" },
		CharacterFPS = 60,
		HighPriorityFPS = 60,
		LowPriorityFPS = 24,
		Skybox = true,
		SkyboxCelestialBodies = Lighting:FindFirstChildOfClass("Sky").CelestialBodiesShown
	}
	assign(opts, vpfBindOptions)

	local id = `VPF-{HttpService:GenerateGUID(false)}`

	local gcElapsedTime = 0

	local characterSyncElapsedTime = 0
	local highPrioritySyncElapsedTime = 0
	local lowPrioritySyncElapsedTime = 0

	local partsFolder = Instance.new(opts.WorldModel)
	partsFolder.Name = "Workspace"
	partsFolder.Parent = vpf

	local skyboxModel = Instance.new("Model")
	skyboxModel.Name = "Skybox"

	local vpfSky = Instance.fromExisting(Lighting:FindFirstChildOfClass("Sky"))
	vpfSky.Parent = vpf

	local skybox = Instance.new("Part")
	skybox.Name = "Skybox"
	skybox.CFrame = SB_CFRAME
	skybox.Size = Vector3.one * (opts.RenderDistance * 2)
	skybox.Transparency = 1
	skybox.Parent = skyboxModel

	local skyboxDn = Instance.new("Part")
	skyboxDn.Name = "SkyboxDn"
	skyboxDn.CFrame = SBDN_CFRAME
	skyboxDn.Size = Vector3.one * (opts.RenderDistance * 2)
	skyboxDn.Transparency = 1
	skyboxDn.Parent = skyboxModel

	for side, face in SKYBOX do
		local decal = Instance.new("Decal")
		decal.Name = side;
		decal.Face = face
		decal.Texture = Lighting:FindFirstChildOfClass("Sky")[side]
		decal.Parent = if side == "SkyboxDn" then skyboxDn else skybox
	end

	skyboxModel.Parent = vpf

	local renderSteppedConnection: RBXScriptConnection,
	destroyConnection: RBXScriptConnection,
	descendantRemovingConnection: RBXScriptConnection

	local connections: { [string]: {RBXScriptConnection} } = {}

	local function destroyVPFPart(vpfPart: any): ()
		for _, connection in connections[vpfPart:GetAttribute("Id")] do
			connection:Disconnect()
		end
		vpfPart.WorkspaceRef.Value:FindFirstChild(id):Destroy()
		vpfPart:Destroy()
	end

	local function createVPFPart(parent: Instance, part: BasePart): BasePart
		local vpfPart = Instance.fromExisting(part)
		vpfPart:SetAttribute("Id", HttpService:GenerateGUID())
		local vpf2workspaceRef = Instance.new("ObjectValue")
		vpf2workspaceRef.Name = "WorkspaceRef"
		vpf2workspaceRef.Value = part
		vpf2workspaceRef.Parent = vpfPart
		vpfPart.Parent = parent
		local workspace2vpfRef = Instance.new("ObjectValue")
		workspace2vpfRef.Archivable = false
		workspace2vpfRef.Name = id
		workspace2vpfRef.Value = vpfPart
		workspace2vpfRef.Parent = part
		return vpfPart
	end

	local function render(deltaTime: number): ()
		if not vpf.CurrentCamera then return end

		if opts.Camera then
			vpf.CurrentCamera.CFrame = opts.Camera.CFrame
		end

		local camera: Camera = opts.Camera or vpf.CurrentCamera
		skybox.Position = camera.CFrame.Position
		skyboxDn.Position = camera.CFrame.Position

		gcElapsedTime += deltaTime
		characterSyncElapsedTime += deltaTime
		highPrioritySyncElapsedTime += deltaTime
		lowPrioritySyncElapsedTime += deltaTime

		local syncCharacters = characterSyncElapsedTime >= 1 / opts.CharacterFPS
		local syncHighPriority = highPrioritySyncElapsedTime >= 1 / opts.HighPriorityFPS
		local syncLowPriority = lowPrioritySyncElapsedTime >= 1 / opts.LowPriorityFPS

		if not syncCharacters and not syncHighPriority and not syncLowPriority then
			return
		end

		if syncCharacters then characterSyncElapsedTime -= 1 / opts.CharacterFPS end
		if syncHighPriority then highPrioritySyncElapsedTime -= 1 / opts.HighPriorityFPS end
		if syncLowPriority then lowPrioritySyncElapsedTime -= 1 / opts.LowPriorityFPS end

		local shouldSync = { syncLowPriority, syncHighPriority, syncCharacters }

		local gc = gcElapsedTime >= opts.GCInterval
		if gc then gcElapsedTime -= opts.GCInterval end

		local parts = workspace:GetPartBoundsInRadius(camera.CFrame.Position, opts.RenderDistance, opts.OverlapParams)

		if gc then
			for _, vpfPart in partsFolder:GetChildren() do
				CollectionService:AddTag(vpfPart, `{id}-GC`)
			end
		end

		for _, part in parts do
			if not part:IsA("BasePart") then continue end

			local partParent: Instance = partsFolder

			local syncPriority = if CollectionService:HasTag(part, "VPF-HighPriority") then 2 else 1

			local model = part.Parent
			if model:IsA("Accessory") or model:IsA("Tool") then
				model = model.Parent
			end

			if model:FindFirstChildWhichIsA("Humanoid") then
				local saved: ObjectValue = model:FindFirstChild(id)
				if saved then
					partParent = saved.Value :: Instance
				else
					local vpfModel = createVPFPart(partParent, model)
					partParent = vpfModel
					for _, child in model:GetChildren() do
						replicateChild(vpfModel, model, ReplicateModelChildren, child, true)
					end
					model.ChildAdded:Connect(function(child)
						replicateChild(vpfModel, model, ReplicateModelChildren, child, true)
					end)
				end
				syncPriority = 3
			end

			if not shouldSync[syncPriority] then continue end

			local saved: ObjectValue = part:FindFirstChild(id)
			if saved then
				local vpfPart = saved.Value :: typeof(part)
				for partType, props in opts.ReplicateHighPriority do
					if part:IsA(partType) then
						for _, prop in props do
							vpfPart[prop] = part[prop]
						end
					end
				end
				CollectionService:RemoveTag(vpfPart, `{id}-GC`)
			else
				local vpfPart = createVPFPart(partParent, part)
				local partConnections = {}
				for partType, props in opts.ReplicateLowPriority do
					if part:IsA(partType) then
						for _, prop in props do
							partConnections[#partConnections + 1] = part:GetPropertyChangedSignal(prop):Connect(function()
								(vpfPart :: any)[prop] = part[prop]
							end)
						end
					end
				end
				connections[vpfPart:GetAttribute("Id")] = partConnections
				for _, child in part:GetChildren() do
					replicateChild(vpfPart, part, opts.ReplicateChildren, child)
				end
				part.ChildAdded:Connect(function(child)
					replicateChild(vpfPart, part, opts.ReplicateChildren, child)
				end)
			end
		end

		if gc then
			for _, vpfPart in CollectionService:GetTagged(`{id}-GC`) do
				if vpfPart:IsA("Model") then
					for _, child in vpfPart:GetChildren() do
						if vpfPart:IsA("BasePart") then
							destroyVPFPart(child)
						end
					end
				else
					destroyVPFPart(vpfPart)
				end
			end
		end
	end

	renderSteppedConnection = RunService.RenderStepped:Connect(render)

	descendantRemovingConnection = workspace.DescendantRemoving:Connect(function(descendant)
		local ref = descendant:FindFirstChild(id)
		if ref then
			destroyVPFPart(ref.Value)
		end
	end)

	local function destroy()
		renderSteppedConnection:Disconnect()
		destroyConnection:Disconnect()
		descendantRemovingConnection:Disconnect()
		for _, vpfPart in partsFolder:GetChildren() do
			(((vpfPart :: any).WorkspaceRef :: ObjectValue).Value :: BasePart):FindFirstChild(id):Destroy()
		end
		for _, partConnections in connections do
			for _, connection in partConnections do
				connection:Disconnect()
			end
		end
		partsFolder:Destroy()
		vpfSky:Destroy()
		skyboxModel:Destroy()
	end
	destroyConnection = vpf.Destroying:Connect(destroy)

	return destroy
end

return VPFBind