123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- //======= Copyright (c) Valve Corporation, All rights reserved. ===============
- //
- // Purpose: Helper for smoothing over transitions between levels.
- //
- //=============================================================================
- using UnityEngine;
- using System.Collections;
- using Valve.VR;
- using System.IO;
- namespace Valve.VR
- {
- public class SteamVR_LoadLevel : MonoBehaviour
- {
- private static SteamVR_LoadLevel _active = null;
- public static bool loading { get { return _active != null; } }
- public static float progress
- {
- get { return (_active != null && _active.async != null) ? _active.async.progress : 0.0f; }
- }
- public static Texture progressTexture
- {
- get { return (_active != null) ? _active.renderTexture : null; }
- }
- // Name of level to load.
- public string levelName;
- // Name of internal process to launch (instead of levelName).
- public string internalProcessPath;
- // The command-line args for the internal process to launch.
- public string internalProcessArgs;
- // If true, call LoadLevelAdditiveAsync instead of LoadLevelAsync.
- public bool loadAdditive;
- // Async load causes crashes in some apps.
- public bool loadAsync = true;
- // Optional logo texture.
- public Texture loadingScreen;
- // Optional progress bar textures.
- public Texture progressBarEmpty, progressBarFull;
- // Sizes of overlays.
- public float loadingScreenWidthInMeters = 6.0f;
- public float progressBarWidthInMeters = 3.0f;
- // If specified, the loading screen will be positioned in the player's view this far away.
- public float loadingScreenDistance = 0.0f;
- // Optional overrides for where to display loading screen and progress bar overlays.
- // Otherwise defaults to using this object's transform.
- public Transform loadingScreenTransform, progressBarTransform;
- // Optional skybox override textures.
- public Texture front, back, left, right, top, bottom;
- // Colors to use when dropping to the compositor between levels if no skybox is set.
- public Color backgroundColor = Color.black;
- // If false, the background color above gets applied as the foreground color in the compositor.
- // This does not have any effect when using a skybox instead.
- public bool showGrid = false;
- // Time to fade from current scene to the compositor and back.
- public float fadeOutTime = 0.5f;
- public float fadeInTime = 0.5f;
- // Additional time to wait after finished loading before we start fading the new scene back in.
- // This is to cover up any initial hitching that takes place right at the start of levels.
- // Most scenes should hopefully not require this.
- public float postLoadSettleTime = 0.0f;
- // Time to fade loading screen in and out (also used for progress bar).
- public float loadingScreenFadeInTime = 1.0f;
- public float loadingScreenFadeOutTime = 0.25f;
- float fadeRate = 1.0f;
- float alpha = 0.0f;
- AsyncOperation async; // used to track level load progress
- RenderTexture renderTexture; // used to render progress bar
- ulong loadingScreenOverlayHandle = OpenVR.k_ulOverlayHandleInvalid;
- ulong progressBarOverlayHandle = OpenVR.k_ulOverlayHandleInvalid;
- public bool autoTriggerOnEnable = false;
- void OnEnable()
- {
- if (autoTriggerOnEnable)
- Trigger();
- }
- public void Trigger()
- {
- if (!loading && !string.IsNullOrEmpty(levelName))
- StartCoroutine(LoadLevel());
- }
- // Helper function to quickly and simply load a level from script.
- public static void Begin(string levelName,
- bool showGrid = false, float fadeOutTime = 0.5f,
- float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f)
- {
- var loader = new GameObject("loader").AddComponent<SteamVR_LoadLevel>();
- loader.levelName = levelName;
- loader.showGrid = showGrid;
- loader.fadeOutTime = fadeOutTime;
- loader.backgroundColor = new Color(r, g, b, a);
- loader.Trigger();
- }
- // Updates progress bar.
- void OnGUI()
- {
- if (_active != this)
- return;
- // Optionally create an overlay for our progress bar to use, separate from the loading screen.
- if (progressBarEmpty != null && progressBarFull != null)
- {
- if (progressBarOverlayHandle == OpenVR.k_ulOverlayHandleInvalid)
- progressBarOverlayHandle = GetOverlayHandle("progressBar", progressBarTransform != null ? progressBarTransform : transform, progressBarWidthInMeters);
- if (progressBarOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- {
- var progress = (async != null) ? async.progress : 0.0f;
- // Use the full bar size for everything.
- var w = progressBarFull.width;
- var h = progressBarFull.height;
- // Create a separate render texture so we can composite the full image on top of the empty one.
- if (renderTexture == null)
- {
- renderTexture = new RenderTexture(w, h, 0);
- renderTexture.Create();
- }
- var prevActive = RenderTexture.active;
- RenderTexture.active = renderTexture;
- if (Event.current.type == EventType.Repaint)
- GL.Clear(false, true, Color.clear);
- GUILayout.BeginArea(new Rect(0, 0, w, h));
- GUI.DrawTexture(new Rect(0, 0, w, h), progressBarEmpty);
- // Reveal the full bar texture based on progress.
- GUI.DrawTextureWithTexCoords(new Rect(0, 0, progress * w, h), progressBarFull, new Rect(0.0f, 0.0f, progress, 1.0f));
- GUILayout.EndArea();
- RenderTexture.active = prevActive;
- // Texture needs to be set every frame after it is updated since SteamVR makes a copy internally to a shared texture.
- var overlay = OpenVR.Overlay;
- if (overlay != null)
- {
- var texture = new Texture_t();
- texture.handle = renderTexture.GetNativeTexturePtr();
- texture.eType = SteamVR.instance.textureType;
- texture.eColorSpace = EColorSpace.Auto;
- overlay.SetOverlayTexture(progressBarOverlayHandle, ref texture);
- }
- }
- }
- #if false
- // Draw loading screen and progress bar to 2d companion window as well.
- if (loadingScreen != null)
- {
- var screenAspect = (float)Screen.width / Screen.height;
- var textureAspect = (float)loadingScreen.width / loadingScreen.height;
- float w, h;
- if (screenAspect < textureAspect)
- {
- // Clamp horizontally
- w = Screen.width * 0.9f;
- h = w / textureAspect;
- }
- else
- {
- // Clamp vertically
- h = Screen.height * 0.9f;
- w = h * textureAspect;
- }
- GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height));
- var x = Screen.width / 2 - w / 2;
- var y = Screen.height / 2 - h / 2;
- GUI.DrawTexture(new Rect(x, y, w, h), loadingScreen);
- GUILayout.EndArea();
- }
- if (renderTexture != null)
- {
- var x = Screen.width / 2 - renderTexture.width / 2;
- var y = Screen.height * 0.9f - renderTexture.height;
- GUI.DrawTexture(new Rect(x, y, renderTexture.width, renderTexture.height), renderTexture);
- }
- #endif
- }
- // Fade our overlays in/out over time.
- void Update()
- {
- if (_active != this)
- return;
- alpha = Mathf.Clamp01(alpha + fadeRate * Time.deltaTime);
- var overlay = OpenVR.Overlay;
- if (overlay != null)
- {
- if (loadingScreenOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- overlay.SetOverlayAlpha(loadingScreenOverlayHandle, alpha);
- if (progressBarOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- overlay.SetOverlayAlpha(progressBarOverlayHandle, alpha);
- }
- }
- // Corourtine to handle all the steps across loading boundaries.
- IEnumerator LoadLevel()
- {
- // Optionally rotate loading screen transform around the camera into view.
- // We assume here that the loading screen is already facing toward the origin,
- // and that the progress bar transform (if any) is a child and will follow along.
- if (loadingScreen != null && loadingScreenDistance > 0.0f)
- {
- Transform hmd = this.transform;
- if (Camera.main != null)
- hmd = Camera.main.transform;
- Quaternion rot = Quaternion.Euler(0.0f, hmd.eulerAngles.y, 0.0f);
- Vector3 pos = hmd.position + (rot * new Vector3(0.0f, 0.0f, loadingScreenDistance));
- var t = loadingScreenTransform != null ? loadingScreenTransform : transform;
- t.position = pos;
- t.rotation = rot;
- }
- _active = this;
- SteamVR_Events.Loading.Send(true);
- // Calculate rate for fading in loading screen and progress bar.
- if (loadingScreenFadeInTime > 0.0f)
- {
- fadeRate = 1.0f / loadingScreenFadeInTime;
- }
- else
- {
- alpha = 1.0f;
- }
- var overlay = OpenVR.Overlay;
- // Optionally create our loading screen overlay.
- if (loadingScreen != null && overlay != null)
- {
- loadingScreenOverlayHandle = GetOverlayHandle("loadingScreen", loadingScreenTransform != null ? loadingScreenTransform : transform, loadingScreenWidthInMeters);
- if (loadingScreenOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- {
- var texture = new Texture_t();
- texture.handle = loadingScreen.GetNativeTexturePtr();
- texture.eType = SteamVR.instance.textureType;
- texture.eColorSpace = EColorSpace.Auto;
- overlay.SetOverlayTexture(loadingScreenOverlayHandle, ref texture);
- }
- }
- bool fadedForeground = false;
- // Fade out to compositor
- SteamVR_Events.LoadingFadeOut.Send(fadeOutTime);
- // Optionally set a skybox to use as a backdrop in the compositor.
- var compositor = OpenVR.Compositor;
- if (compositor != null)
- {
- if (front != null)
- {
- SteamVR_Skybox.SetOverride(front, back, left, right, top, bottom);
- // Explicitly fade to the compositor since loading will cause us to stop rendering.
- compositor.FadeGrid(fadeOutTime, true);
- yield return new WaitForSeconds(fadeOutTime);
- }
- else if (backgroundColor != Color.clear)
- {
- // Otherwise, use the specified background color.
- if (showGrid)
- {
- // Set compositor background color immediately, and start fading to it.
- compositor.FadeToColor(0.0f, backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a, true);
- compositor.FadeGrid(fadeOutTime, true);
- yield return new WaitForSeconds(fadeOutTime);
- }
- else
- {
- // Fade the foreground color in (which will blend on top of the scene), and then cut to the compositor.
- compositor.FadeToColor(fadeOutTime, backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a, false);
- yield return new WaitForSeconds(fadeOutTime + 0.1f);
- compositor.FadeGrid(0.0f, true);
- fadedForeground = true;
- }
- }
- }
- // Now that we're fully faded out, we can stop submitting frames to the compositor.
- SteamVR_Render.pauseRendering = true;
- // Continue waiting for the overlays to fully fade in before continuing.
- while (alpha < 1.0f)
- yield return null;
- // Keep us from getting destroyed when loading the new level, otherwise this coroutine will get stopped prematurely.
- transform.parent = null;
- DontDestroyOnLoad(gameObject);
- if (!string.IsNullOrEmpty(internalProcessPath))
- {
- Debug.Log("<b>[SteamVR]</b> Launching external application...");
- var applications = OpenVR.Applications;
- if (applications == null)
- {
- Debug.Log("<b>[SteamVR]</b> Failed to get OpenVR.Applications interface!");
- }
- else
- {
- var workingDirectory = Directory.GetCurrentDirectory();
- var fullPath = Path.Combine(workingDirectory, internalProcessPath);
- Debug.Log("<b>[SteamVR]</b> LaunchingInternalProcess");
- Debug.Log("<b>[SteamVR]</b> ExternalAppPath = " + internalProcessPath);
- Debug.Log("<b>[SteamVR]</b> FullPath = " + fullPath);
- Debug.Log("<b>[SteamVR]</b> ExternalAppArgs = " + internalProcessArgs);
- Debug.Log("<b>[SteamVR]</b> WorkingDirectory = " + workingDirectory);
- var error = applications.LaunchInternalProcess(fullPath, internalProcessArgs, workingDirectory);
- Debug.Log("<b>[SteamVR]</b> LaunchInternalProcessError: " + error);
- #if UNITY_EDITOR
- UnityEditor.EditorApplication.isPlaying = false;
- #elif !UNITY_METRO
- System.Diagnostics.Process.GetCurrentProcess().Kill();
- #endif
- }
- }
- else
- {
- var mode = loadAdditive ? UnityEngine.SceneManagement.LoadSceneMode.Additive : UnityEngine.SceneManagement.LoadSceneMode.Single;
- if (loadAsync)
- {
- Application.backgroundLoadingPriority = ThreadPriority.Low;
- async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(levelName, mode);
- // Performing this in a while loop instead seems to help smooth things out.
- //yield return async;
- while (!async.isDone)
- {
- yield return null;
- }
- }
- else
- {
- UnityEngine.SceneManagement.SceneManager.LoadScene(levelName, mode);
- }
- }
- yield return null;
- System.GC.Collect();
- yield return null;
- Shader.WarmupAllShaders();
- // Optionally wait a short period of time after loading everything back in, but before we start rendering again
- // in order to give everything a change to settle down to avoid any hitching at the start of the new level.
- yield return new WaitForSeconds(postLoadSettleTime);
- SteamVR_Render.pauseRendering = false;
- // Fade out loading screen.
- if (loadingScreenFadeOutTime > 0.0f)
- {
- fadeRate = -1.0f / loadingScreenFadeOutTime;
- }
- else
- {
- alpha = 0.0f;
- }
- // Fade out to compositor
- SteamVR_Events.LoadingFadeIn.Send(fadeInTime);
- // Refresh compositor reference since loading scenes might have invalidated it.
- compositor = OpenVR.Compositor;
- if (compositor != null)
- {
- // Fade out foreground color if necessary.
- if (fadedForeground)
- {
- compositor.FadeGrid(0.0f, false);
- compositor.FadeToColor(fadeInTime, 0.0f, 0.0f, 0.0f, 0.0f, false);
- yield return new WaitForSeconds(fadeInTime);
- }
- else
- {
- // Fade scene back in, and reset skybox once no longer visible.
- compositor.FadeGrid(fadeInTime, false);
- yield return new WaitForSeconds(fadeInTime);
- if (front != null)
- {
- SteamVR_Skybox.ClearOverride();
- }
- }
- }
- // Finally, stick around long enough for our overlays to fully fade out.
- while (alpha > 0.0f)
- yield return null;
- if (overlay != null)
- {
- if (progressBarOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- overlay.HideOverlay(progressBarOverlayHandle);
- if (loadingScreenOverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
- overlay.HideOverlay(loadingScreenOverlayHandle);
- }
- Destroy(gameObject);
- _active = null;
- SteamVR_Events.Loading.Send(false);
- }
- // Helper to create (or reuse if possible) each of our different overlay types.
- ulong GetOverlayHandle(string overlayName, Transform transform, float widthInMeters = 1.0f)
- {
- ulong handle = OpenVR.k_ulOverlayHandleInvalid;
- var overlay = OpenVR.Overlay;
- if (overlay == null)
- return handle;
- var key = SteamVR_Overlay.key + "." + overlayName;
- var error = overlay.FindOverlay(key, ref handle);
- if (error != EVROverlayError.None)
- error = overlay.CreateOverlay(key, overlayName, ref handle);
- if (error == EVROverlayError.None)
- {
- overlay.ShowOverlay(handle);
- overlay.SetOverlayAlpha(handle, alpha);
- overlay.SetOverlayWidthInMeters(handle, widthInMeters);
- // D3D textures are upside-down in Unity to match OpenGL.
- if (SteamVR.instance.textureType == ETextureType.DirectX)
- {
- var textureBounds = new VRTextureBounds_t();
- textureBounds.uMin = 0;
- textureBounds.vMin = 1;
- textureBounds.uMax = 1;
- textureBounds.vMax = 0;
- overlay.SetOverlayTextureBounds(handle, ref textureBounds);
- }
- // Convert from world space to tracking space using the top-most camera.
- var vrcam = (loadingScreenDistance == 0.0f) ? SteamVR_Render.Top() : null;
- if (vrcam != null && vrcam.origin != null)
- {
- var offset = new SteamVR_Utils.RigidTransform(vrcam.origin, transform);
- offset.pos.x /= vrcam.origin.localScale.x;
- offset.pos.y /= vrcam.origin.localScale.y;
- offset.pos.z /= vrcam.origin.localScale.z;
- var t = offset.ToHmdMatrix34();
- overlay.SetOverlayTransformAbsolute(handle, SteamVR.settings.trackingSpace, ref t);
- }
- else
- {
- var t = new SteamVR_Utils.RigidTransform(transform).ToHmdMatrix34();
- overlay.SetOverlayTransformAbsolute(handle, SteamVR.settings.trackingSpace, ref t);
- }
- }
- return handle;
- }
- }
- }
|