--Dialog: 3D dialogs for ROBLOX in VR --written by 0xBAADF00D --6/30/2016 local CoreGui = game:GetService("CoreGui") local RobloxGui = CoreGui:WaitForChild("RobloxGui") local InputService = game:GetService("UserInputService") local RunService = game:GetService("RunService") local Utility = require(RobloxGui.Modules.Settings.Utility) local Panel3D = require(RobloxGui.Modules.VR.Panel3D) local DIALOG_BG_COLOR = Color3.new(0.2, 0.2, 0.2) local DIALOG_BG_TRANSPARENCY = 0.3 local DIALOG_TITLE_HEIGHT = 66 local DIALOG_COLOR_HEIGHT = 8 local DIALOG_TITLE_TEXT_SIZE = Enum.FontSize.Size36 local DIALOG_CONTENT_PADDING = 48 local TITLE_COLOR = Color3.new(1, 1, 1) local PANEL_OFFSET_CF = CFrame.new(0, 0, -7) * CFrame.Angles(0, math.pi, 0) local emptySelectionImage = Utility:Create "ImageLabel" { Name = "EmptySelectionImage", Image = "", BackgroundTransparency = 1, ImageTransparency = 1 } local DialogPanel = Panel3D.Get("Dialog") DialogPanel:SetType(Panel3D.Type.Fixed) DialogPanel.localCF = PANEL_OFFSET_CF DialogPanel:SetCanFade(true) local dialogPanelAngle = 0 local opacityLookup = {} local resetBaseAngle = false local baseAngle = 0 local PANEL_FADE_ANGLE_0, PANEL_FADE_ANGLE_1 = math.rad(37.5), math.rad(44) local PANEL_FADE_RANGE = PANEL_FADE_ANGLE_1 - PANEL_FADE_ANGLE_0 local PANEL_REAPPEAR_ANGLE = math.rad(90) local function positionDialogPanel(desiredAngle) dialogPanelAngle = desiredAngle local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head) local headPos = headCF.p DialogPanel.localCF = CFrame.new(headPos) * CFrame.Angles(0, desiredAngle, 0) * PANEL_OFFSET_CF end function DialogPanel:CalculateTransparency() local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head) local headLook = headCF.lookVector * Vector3.new(1, 0, 1) local vertAngle = math.asin(headCF.lookVector.Y) local vectorToPanel = Vector3.new(math.cos(baseAngle + dialogPanelAngle + math.rad(90)), 0, -math.sin(baseAngle + dialogPanelAngle + math.rad(90))) if math.abs(vertAngle) > PANEL_FADE_ANGLE_1 then resetBaseAngle = true end local angleToPanel = math.acos(headLook:Dot(vectorToPanel)) return math.min(math.max(0, (angleToPanel - PANEL_FADE_ANGLE_0) / PANEL_FADE_RANGE), 1) end local function updatePanelPosition() local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head) local headLook = headCF.lookVector * Vector3.new(1, 0, 1) local headAngle = (math.atan2(-headLook.Z, headLook.X) - math.rad(90)) % math.rad(360) local newPanelAngle = baseAngle + math.floor((headAngle / PANEL_REAPPEAR_ANGLE) + 0.5) * PANEL_REAPPEAR_ANGLE if resetBaseAngle then resetBaseAngle = false baseAngle = headAngle end positionDialogPanel(newPanelAngle) end -- RenderStep update function for the DialogPanel function dialogPanelRenderUpdate() if DialogPanel.transparency == 1 or resetBaseAngle then updatePanelPosition() end --update the transparency of gui elements local opacityMult = 1 - DialogPanel.transparency for guiElement, baseOpacity in pairs(opacityLookup) do local transparency = 1 - (baseOpacity * opacityMult) if guiElement:IsA("TextLabel") or guiElement:IsA("TextButton") then guiElement.TextTransparency = transparency elseif guiElement:IsA("ImageLabel") or guiElement:IsA("ImageButton") then guiElement.ImageTransparency = transparency elseif guiElement:IsA("Frame") then guiElement.BackgroundTransparency = transparency end end end local DialogQueue = {} local currentDescendantConn = nil local lastDialogShown = nil local function updatePanel() local currentDialog = DialogQueue[1] if lastDialogShown and lastDialogShown.content then lastDialogShown.content.Parent = nil end if not currentDialog or not currentDialog.content then DialogPanel:SetVisible(false) opacityLookup = {} if currentDescendantConn then currentDescendantConn:disconnect() currentDescendantConn = nil end else currentDialog.content.Parent = DialogPanel:GetGUI() lastDialogShown = currentDialog local contentSize = currentDialog.content.AbsoluteSize DialogPanel:ResizePixels(contentSize.X, contentSize.Y, 100) resetBaseAngle = true updatePanelPosition() DialogPanel:SetVisible(true) opacityLookup = {} local function search(parent) if parent:IsA("ImageLabel") or parent:IsA("ImageButton") then opacityLookup[parent] = 1 - parent.ImageTransparency elseif parent:IsA("TextLabel") or parent:IsA("TextButton") then opacityLookup[parent] = 1 - parent.TextTransparency elseif parent:IsA("Frame") then opacityLookup[parent] = 1 - parent.BackgroundTransparency end for i, v in pairs(parent:GetChildren()) do search(v) end end search(DialogPanel:GetGUI()) if currentDescendantConn then currentDescendantConn:disconnect() currentDescendantConn = nil end currentDescendantConn = DialogPanel:GetGUI().DescendantAdded:connect(function(descendant) search(descendant) end) end end local Dialog = {} Dialog.__index = Dialog function Dialog.new(content) local self = setmetatable({}, Dialog) self.content = content return self end function Dialog:SetContent(guiElement) if not guiElement and self.content then self.content.Parent = nil end self.content = guiElement end local renderFuncBound = false function Dialog:Show(shouldTakeover) if not renderFuncBound then renderFuncBound = true RunService:BindToRenderStep("DialogPanel", Enum.RenderPriority.First.Value, dialogPanelRenderUpdate) end if shouldTakeover then table.insert(DialogQueue, 1, self) else table.insert(DialogQueue, self) end updatePanel() end function Dialog:Close() for idx, dialog in pairs(DialogQueue) do if dialog == self then table.remove(DialogQueue, idx) break end end if renderFuncBound and #DialogQueue == 0 then renderFuncBound = false RunService:UnbindFromRenderStep("DialogPanel") end updatePanel() end return Dialog