-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathPassthroughFader.cs
296 lines (254 loc) · 10.2 KB
/
PassthroughFader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// Copyright (c) Meta Platforms, Inc. and affiliates.
using System;
using System.Collections;
using MRMotifs.SharedAssets;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MRMotifs.PassthroughTransitioning
{
/// <summary>
/// A unified passthrough fader that supports both Selective and Underlay modes.
/// Select the mode in the inspector via the Passthrough Viewing Mode property.
/// </summary>
public class PassthroughFader : MonoBehaviour
{
/// <summary>
/// The direction in which the fade effect will occur.
/// </summary>
private enum FadeDirection
{
Normal,
RightToLeft,
TopToBottom,
InsideOut
}
/// <summary>
/// The viewing mode for passthrough. Select "Underlay" to have the effect apply over the entire view
/// or "Selective" to limit it to a sphere defined by selectiveDistance.
/// </summary>
private enum PassthroughViewingMode
{
Underlay,
Selective
}
/// <summary>
/// Internal state of the fader determined by the target alpha.
/// </summary>
private enum FaderState
{
MR,
VR,
InTransition
}
/// <summary>
/// The fader state is derived from _targetAlpha.
/// </summary>
private FaderState State => Mathf.Approximately(m_targetAlpha, 1f) ? FaderState.MR :
Mathf.Approximately(m_targetAlpha, 0f) ? FaderState.VR :
FaderState.InTransition;
[Header("Passthrough Fader Settings")]
[Tooltip("The passthrough layer used for the fade effect.")]
[SerializeField]
private OVRPassthroughLayer oVRPassthroughLayer;
[Tooltip("Select Underlay to fade the entire view or Selective for a limited sphere.")]
[SerializeField]
private PassthroughViewingMode passthroughViewingMode = PassthroughViewingMode.Selective;
[Tooltip("Size/range of the passthrough fader sphere (used in Selective mode).")]
[Range(0.01f, 100f)]
[SerializeField]
private float selectiveDistance = 5f;
[Tooltip("The speed of the fade effect.")]
[SerializeField]
private float fadeSpeed = 1f;
[Tooltip("The direction of the fade effect.")]
[SerializeField]
private FadeDirection fadeDirection = FadeDirection.TopToBottom;
[Header("Fade Events")]
[Tooltip("Event triggered when the fade in starts.")]
[SerializeField]
private UnityEvent onStartFadeIn = new();
[Tooltip("Event triggered when the fade out starts.")]
[SerializeField]
private UnityEvent onStartFadeOut = new();
[Tooltip("Event triggered when the fade in has ended.")]
[SerializeField]
private UnityEvent onFadeInComplete = new();
[Tooltip("Event triggered when the fade out has ended.")]
[SerializeField]
private UnityEvent onFadeOutComplete = new();
private Camera m_mainCamera;
private Material m_material;
private MeshRenderer m_meshRenderer;
private MenuPanel m_menuPanel;
private Button m_passthroughButton;
private Color m_skyboxBackgroundColor;
private float m_targetAlpha;
private const float FADE_TOLERANCE = 0.001f;
private static readonly int s_invertedAlpha = Shader.PropertyToID("_InvertedAlpha");
private static readonly int s_direction = Shader.PropertyToID("_FadeDirection");
private void Awake()
{
m_mainCamera = Camera.main;
if (m_mainCamera != null)
{
m_skyboxBackgroundColor = m_mainCamera.backgroundColor;
}
// Disable premultiplied alpha blending for better underlay blending.
OVRManager.eyeFovPremultipliedAlphaModeEnabled = false;
m_meshRenderer = GetComponent<MeshRenderer>();
m_material = m_meshRenderer.material;
m_menuPanel = FindAnyObjectByType<MenuPanel>();
if (m_menuPanel != null)
{
m_passthroughButton = m_menuPanel.PassthroughFaderButton;
m_passthroughButton.onClick.AddListener(TogglePassthrough);
}
oVRPassthroughLayer.passthroughLayerResumed.AddListener(OnPassthroughLayerResumed);
SetupPassthrough();
#if UNITY_ANDROID
CheckIfPassthroughIsRecommended();
#endif
}
private void OnDestroy()
{
if (m_menuPanel != null)
{
m_passthroughButton.onClick.RemoveListener(TogglePassthrough);
}
oVRPassthroughLayer.passthroughLayerResumed.RemoveListener(OnPassthroughLayerResumed);
}
/// <summary>
/// Sets up the passthrough based on the selected viewing mode.
/// </summary>
private void SetupPassthrough()
{
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
var maxCamView = m_mainCamera.farClipPlane - 0.01f;
transform.localScale = new Vector3(maxCamView, maxCamView, maxCamView);
m_meshRenderer.enabled = false;
}
else // Selective
{
transform.localScale = new Vector3(selectiveDistance, selectiveDistance, selectiveDistance);
m_meshRenderer.enabled = true;
}
}
/// <summary>
/// Checks if passthrough is recommended and adjusts the camera and material settings.
/// </summary>
private void CheckIfPassthroughIsRecommended()
{
if (m_mainCamera == null) return;
if (OVRManager.IsPassthroughRecommended())
{
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
m_mainCamera.clearFlags = CameraClearFlags.SolidColor;
m_mainCamera.backgroundColor = Color.clear;
}
else
{
m_mainCamera.clearFlags = CameraClearFlags.Skybox;
m_mainCamera.backgroundColor = m_skyboxBackgroundColor;
}
m_material.SetFloat(s_invertedAlpha, 1);
}
else
{
oVRPassthroughLayer.enabled = false;
m_mainCamera.clearFlags = CameraClearFlags.Skybox;
m_mainCamera.backgroundColor = m_skyboxBackgroundColor;
m_material.SetFloat(s_invertedAlpha, 0);
}
}
/// <summary>
/// Called (for example, by a UI button) to toggle between passthrough states.
/// </summary>
public void TogglePassthrough()
{
UpdateFadeDirection();
switch (State)
{
case FaderState.MR:
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
m_meshRenderer.enabled = true;
m_mainCamera.clearFlags = CameraClearFlags.Skybox;
m_mainCamera.backgroundColor = m_skyboxBackgroundColor;
}
m_targetAlpha = 0;
onStartFadeOut?.Invoke();
StopAllCoroutines();
StartCoroutine(FadeToTarget());
break;
case FaderState.VR:
oVRPassthroughLayer.enabled = true;
onStartFadeIn?.Invoke();
break;
case FaderState.InTransition:
m_targetAlpha = Mathf.Approximately(m_targetAlpha, 0f) ? 1f : 0f;
var fadeEvent = Mathf.Approximately(m_targetAlpha, 0f) ? onStartFadeOut : onStartFadeIn;
fadeEvent?.Invoke();
StartCoroutine(FadeToTarget());
break;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Updates the shader’s fade direction property.
/// </summary>
private void UpdateFadeDirection()
{
m_material.SetInt(s_direction, (int)fadeDirection);
}
/// <summary>
/// Listener for when the passthrough layer is resumed.
/// </summary>
private void OnPassthroughLayerResumed(OVRPassthroughLayer passthroughLayer)
{
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
m_meshRenderer.enabled = true;
}
m_targetAlpha = 1;
StopAllCoroutines();
StartCoroutine(FadeToTarget());
}
/// <summary>
/// Fades the material’s alpha toward the target value.
/// </summary>
private IEnumerator FadeToTarget()
{
var currentAlpha = m_material.GetFloat(s_invertedAlpha);
while (Mathf.Abs(currentAlpha - m_targetAlpha) > FADE_TOLERANCE)
{
currentAlpha = Mathf.MoveTowards(currentAlpha, m_targetAlpha, fadeSpeed * Time.deltaTime);
m_material.SetFloat(s_invertedAlpha, currentAlpha);
yield return null;
}
if (Mathf.Abs(m_targetAlpha - 1f) < FADE_TOLERANCE)
{
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
m_mainCamera.clearFlags = CameraClearFlags.SolidColor;
m_mainCamera.backgroundColor = Color.clear;
}
onFadeInComplete?.Invoke();
}
else
{
oVRPassthroughLayer.enabled = false;
if (passthroughViewingMode == PassthroughViewingMode.Underlay)
{
m_mainCamera.clearFlags = CameraClearFlags.Skybox;
m_mainCamera.backgroundColor = m_skyboxBackgroundColor;
}
onFadeOutComplete?.Invoke();
}
m_meshRenderer.enabled = (passthroughViewingMode != PassthroughViewingMode.Underlay);
}
}
}