function ViewportWindow.Destroy(t)t.SurfaceGUI:Destroy()end function ViewportWindow.AddSkybox(f,t)if t and t:IsA("\083\107\121")then f.SkyboxFrame:ClearAllChildren();(SkyboxModel(t)).Parent=f.SkyboxFrame end end function ViewportWindow.AddWithLookup(g,t,f,h,x)x=x or{}h=h or g.ViewportFrame f=f or defaultCloneFunc for t,W in next,t do if not W:IsA("\076\117\097\083\111\117\114\099\101\067\111\110\116\097\105\110\101\114")then local t=W.Archivable W.Archivable=true local a=W:Clone()if a then a:ClearAllChildren()f(W,a)a.Parent=h g:AddWithLookup(W:GetChildren(),f,a,x)x[W]=a end W.Archivable=t end end return x end function ViewportWindow.GetPart(t)return t.SurfaceGUI.Adornee end function ViewportWindow.GetSurfaceInfo(t)local f=t.SurfaceGUI.Adornee local h,x=f.CFrame,f.Size local g=-Vector3.FromNormalId(t.SurfaceGUI.Face)local W=math.abs(g.y)==1 and Vector3.new(g.y,0,0)or UNIT_Y local a=CFrame.fromAxisAngle(W,PI2)*g local U=(g:Cross(a)).Unit local T=h*CFrame.fromMatrix((-g*x)/2,a,U,g)local v=Vector3.new((x*a).Magnitude,(x*U).Magnitude,(x*g).Magnitude)return T,v end function ViewportWindow.RenderFrame(x,t,f,h)local g=game.Workspace.CurrentCamera t=t or g.CFrame if not(f and h)then f,h=x:GetSurfaceInfo()end local W=f*Vector3.new(0,h.y/2,0)local a=f*Vector3.new(0,-h.y/2,0)local U=t.LookVector:Cross(f.UpVector)local T=U:Dot(U)>0 and U.Unit or t.RightVector local v=CFrame.fromMatrix(t.p,T,f.UpVector,T:Cross(f.UpVector))local J=v:Inverse()local E=J*a local k=J*W local i=(E*YZ).Unit local b=(k*YZ).Unit local I=math.sign(i.y)*math.acos(i:Dot(UNIT_NZ))local m=math.sign(b.y)*math.acos(b:Dot(UNIT_NZ))local o=2*math.tan(math.rad(g.FieldOfView)/2)local d=math.tan(m)-math.tan(I)local X=d/o local w=f:VectorToObjectSpace(f.p-t.p)local p=w*XZ local L=w*YZ local O=p.Unit:Dot(UNIT_NZ)local K=f:VectorToObjectSpace(t.LookVector)*XZ local q=K.Unit:Dot(p.Unit)/UNIT_NZ:Dot(p.Unit)local l=math.sqrt(1-O*O)/O local Q,Y=1,h.x/h.y local D=math.sign(w.x*w.z)*l local s=(L.y/L.z)*Y local M=math.abs((q*X)*Y)local F=((f-f.p)*Y_SPIN)*CFrame.new(0,0,0,Q,0,0,0,Y,0,D,s,M)local P={F:GetComponents()}local c={}for t=1,#P,1 do c[t]=math.abs(P[t])end c=math.max(unpack(c))F=CFrame.new(P[1],P[2],P[3],P[4]/c,P[5]/c,P[6]/c,P[7]/c,P[8]/c,P[9]/c,P[10]/c,P[11]/c,P[12]/c)local A=F+t.p x.Camera.FieldOfView=g.FieldOfView x.Camera.CFrame=A x.Camera.Focus=A*CFrame.new(0,0,(t:PointToObjectSpace(f.p)).z)end


function ViewportWindow:Destroy()
	self.SurfaceGUI:Destroy()
end

function ViewportWindow:AddSkybox(skybox)
	if (skybox and skybox:IsA("Sky")) then
		self.SkyboxFrame:ClearAllChildren()
		SkyboxModel(skybox).Parent = self.SkyboxFrame
	end
end

function ViewportWindow:AddWithLookup(children, cloneFunc, parent, lookup)
	lookup = lookup or {}
	parent = parent or self.ViewportFrame
	cloneFunc = cloneFunc or defaultCloneFunc
	
	for _, part in next, children do
		if (not part:IsA("LuaSourceContainer")) then
			local archivable = part.Archivable
			part.Archivable = true
			
			local clone = part:Clone()
			if (clone) then
				clone:ClearAllChildren()
				cloneFunc(part, clone)
				clone.Parent = parent
		
				self:AddWithLookup(part:GetChildren(), cloneFunc, clone, lookup)
				
				lookup[part] = clone
			end
			
			part.Archivable = archivable
		end
	end
	
	return lookup
end

function ViewportWindow:GetPart()
	return self.SurfaceGUI.Adornee
end

function ViewportWindow:GetSurfaceInfo()
	local part = self.SurfaceGUI.Adornee
	local partCF, partSize = part.CFrame, part.Size
	
	local back = -Vector3.FromNormalId(self.SurfaceGUI.Face)
	local axis = (math.abs(back.y) == 1) and Vector3.new(back.y, 0, 0) or UNIT_Y
	local right = CFrame.fromAxisAngle(axis, PI2) * back
	local top = back:Cross(right).Unit
	
	local cf = partCF * CFrame.fromMatrix(-back*partSize/2, right, top, back)
	local size = Vector3.new((partSize * right).Magnitude, (partSize * top).Magnitude, (partSize * back).Magnitude)

	return cf, size
end

function ViewportWindow:RenderFrame(camCF, surfaceCF, surfaceSize)
	local camera = game.Workspace.CurrentCamera
	
	camCF = camCF or camera.CFrame
	if not (surfaceCF and surfaceSize) then
		surfaceCF, surfaceSize = self:GetSurfaceInfo()
	end
	
	--
	
	local tc = surfaceCF * Vector3.new(0, surfaceSize.y/2, 0)
	local bc = surfaceCF * Vector3.new(0, -surfaceSize.y/2, 0)
	
	local cross = camCF.LookVector:Cross(surfaceCF.UpVector)
	local right = cross:Dot(cross) > 0 and cross.Unit or camCF.RightVector
	
	local levelCamCF = CFrame.fromMatrix(camCF.p, right, surfaceCF.UpVector, right:Cross(surfaceCF.UpVector))
	local levelCamCFInv = levelCamCF:Inverse()
	
	local csbc = levelCamCFInv * bc
	local cstc = levelCamCFInv * tc
	local v1 = (csbc*YZ).Unit
	local v2 = (cstc*YZ).Unit
	local alpha = math.sign(v1.y)*math.acos(v1:Dot(UNIT_NZ))
	local beta = math.sign(v2.y)*math.acos(v2:Dot(UNIT_NZ))
	
	local fh = 2*math.tan(math.rad(camera.FieldOfView)/2)
	local hPrime = math.tan(beta) - math.tan(alpha)
	local refHeight = hPrime / fh
	
	--
	
	local c2p = surfaceCF:VectorToObjectSpace(surfaceCF.p - camCF.p)
	local c2pXZ = c2p * XZ
	local c2pYZ = c2p * YZ
	
	local dpX = c2pXZ.Unit:Dot(UNIT_NZ)
	local camXZ = (surfaceCF:VectorToObjectSpace(camCF.LookVector) * XZ)
	
	local scale = camXZ.Unit:Dot(c2pXZ.Unit) / UNIT_NZ:Dot(c2pXZ.Unit)
	local tanArcCos = math.sqrt(1 - dpX*dpX) / dpX
	
	--
	
	local w, h = 1, (surfaceSize.x / surfaceSize.y)
	local dx = math.sign(c2p.x*c2p.z)*tanArcCos
	local dy = c2pYZ.y / c2pYZ.z * h
	local d = math.abs(scale * refHeight * h)
	
	local ncf = (surfaceCF - surfaceCF.p) * Y_SPIN * CFrame.new(0, 0, 0, w, 0, 0, 0, h, 0, dx, dy, d)
	local c = {ncf:GetComponents()}
	
	local max = {}
	for i = 1, #c do max[i] = math.abs(c[i]) end
	max = math.max(unpack(max))
	
	ncf = CFrame.new(c[1], c[2], c[3], c[4]/max, c[5]/max, c[6]/max, c[7]/max, c[8]/max, c[9]/max, c[10]/max, c[11]/max, c[12]/max)
	
	--
	
	--[[
	-- can set w and h to 1 and use this as an alternative scaling method, but I find the above is better
	local ratioXY = (surfaceSize.x / surfaceSize.y)
	local ratioYX = (surfaceSize.y / surfaceSize.x)
	
	if (ratioXY > ratioYX) then
		self.SurfaceGUI.CanvasSize = Vector2.new(1024, 1024 * ratioYX)
	else
		self.SurfaceGUI.CanvasSize = Vector2.new(1024 * ratioXY, 1024)
	end
	--]]
	
	local ncamCF = ncf + camCF.p
	self.Camera.FieldOfView = camera.FieldOfView
	self.Camera.CFrame = ncamCF
	self.Camera.Focus = ncamCF * CFrame.new(0, 0, camCF:PointToObjectSpace(surfaceCF.p).z)
end
