8 using System.Collections;
9 using System.Collections.Generic;
12 namespace Valve.VR.InteractionSystem
17 public Material controllerMaterial;
18 public Color flashColor =
new Color( 1.0f, 0.557f, 0.0f );
19 public GameObject textHintPrefab;
22 public bool debugHints =
false;
27 private List<MeshRenderer> renderers =
new List<MeshRenderer>();
28 private List<MeshRenderer> flashingRenderers =
new List<MeshRenderer>();
29 private float startTime;
30 private float tickCount;
32 private enum OffsetType
41 private class ButtonHintInfo
43 public string componentName;
44 public List<MeshRenderer> renderers;
45 public Transform localTransform;
48 public GameObject textHintObject;
49 public Transform textStartAnchor;
50 public Transform textEndAnchor;
51 public Vector3 textEndOffsetDir;
52 public Transform canvasOffset;
55 public TextMesh textMesh;
56 public Canvas textCanvas;
57 public LineRenderer line;
59 public float distanceFromCenter;
60 public bool textHintActive =
false;
63 private Dictionary<EVRButtonId, ButtonHintInfo> buttonHintInfos;
64 private Transform textHintParent;
65 private List<KeyValuePair<string, ulong>> componentButtonMasks =
new List<KeyValuePair<string, ulong>>();
69 public bool initialized {
get;
private set; }
70 private Vector3 centerPosition = Vector3.zero;
78 renderModelLoadedAction = SteamVR_Events.RenderModelLoadedAction( OnRenderModelLoaded );
79 colorID = Shader.PropertyToID(
"_Color" );
86 player = Player.instance;
91 private void HintDebugLog(
string msg )
95 Debug.Log(
"Hints: " + msg );
103 renderModelLoadedAction.enabled =
true;
110 renderModelLoadedAction.enabled =
false;
116 private void OnParentHandInputFocusLost()
119 HideAllButtonHints();
127 private void OnHandInitialized(
int deviceIndex )
131 renderModel.transform.parent = transform;
132 renderModel.transform.localPosition = Vector3.zero;
133 renderModel.transform.localRotation = Quaternion.identity;
134 renderModel.transform.localScale = Vector3.one;
135 renderModel.SetDeviceIndex( deviceIndex );
140 renderModel.gameObject.SetActive( true );
149 if ( renderModel == this.renderModel )
151 textHintParent =
new GameObject(
"Text Hints" ).transform;
152 textHintParent.SetParent( this.transform );
153 textHintParent.localPosition = Vector3.zero;
154 textHintParent.localRotation = Quaternion.identity;
155 textHintParent.localScale = Vector3.one;
160 var renderModels = holder.instance;
161 if ( renderModels != null )
163 string renderModelDebug =
"Components for render model " + renderModel.index;
164 foreach ( Transform child
in renderModel.transform )
166 ulong buttonMask = renderModels.GetComponentButtonMask( renderModel.renderModelName, child.name );
168 componentButtonMasks.Add(
new KeyValuePair<string, ulong>( child.name, buttonMask ) );
170 renderModelDebug +=
"\n\t" + child.name +
": " + buttonMask;
174 HintDebugLog( renderModelDebug );
178 buttonHintInfos =
new Dictionary<EVRButtonId, ButtonHintInfo>();
180 CreateAndAddButtonInfo( EVRButtonId.k_EButton_SteamVR_Trigger );
181 CreateAndAddButtonInfo( EVRButtonId.k_EButton_ApplicationMenu );
182 CreateAndAddButtonInfo( EVRButtonId.k_EButton_System );
183 CreateAndAddButtonInfo( EVRButtonId.k_EButton_Grip );
184 CreateAndAddButtonInfo( EVRButtonId.k_EButton_SteamVR_Touchpad );
185 CreateAndAddButtonInfo( EVRButtonId.k_EButton_A );
187 ComputeTextEndTransforms();
192 renderModel.gameObject.SetActive( false );
198 private void CreateAndAddButtonInfo( EVRButtonId buttonID )
200 Transform buttonTransform = null;
201 List<MeshRenderer> buttonRenderers =
new List<MeshRenderer>();
203 string buttonDebug =
"Looking for button: " + buttonID;
205 EVRButtonId searchButtonID = buttonID;
206 if ( buttonID == EVRButtonId.k_EButton_Grip &&
SteamVR.instance.hmd_TrackingSystemName.ToLowerInvariant().Contains(
"oculus" ) )
208 searchButtonID = EVRButtonId.k_EButton_Axis2;
210 ulong buttonMaskForID = ( 1ul << (int)searchButtonID );
212 foreach ( KeyValuePair<string, ulong> componentButtonMask
in componentButtonMasks )
214 if ( ( componentButtonMask.Value & buttonMaskForID ) == buttonMaskForID )
216 buttonDebug +=
"\nFound component: " + componentButtonMask.Key +
" " + componentButtonMask.Value;
217 Transform componentTransform = renderModel.FindComponent( componentButtonMask.Key );
219 buttonTransform = componentTransform;
221 buttonDebug +=
"\nFound componentTransform: " + componentTransform +
" buttonTransform: " + buttonTransform;
223 buttonRenderers.AddRange( componentTransform.GetComponentsInChildren<MeshRenderer>() );
227 buttonDebug +=
"\nFound " + buttonRenderers.Count +
" renderers for " + buttonID;
228 foreach ( MeshRenderer renderer
in buttonRenderers )
230 buttonDebug +=
"\n\t" + renderer.name;
233 HintDebugLog( buttonDebug );
235 if ( buttonTransform == null )
237 HintDebugLog(
"Couldn't find buttonTransform for " + buttonID );
241 ButtonHintInfo hintInfo =
new ButtonHintInfo();
242 buttonHintInfos.Add( buttonID, hintInfo );
244 hintInfo.componentName = buttonTransform.name;
245 hintInfo.renderers = buttonRenderers;
248 hintInfo.localTransform = buttonTransform.Find( SteamVR_RenderModel.k_localTransformName );
250 OffsetType offsetType = OffsetType.Right;
253 case EVRButtonId.k_EButton_SteamVR_Trigger:
255 offsetType = OffsetType.Right;
258 case EVRButtonId.k_EButton_ApplicationMenu:
260 offsetType = OffsetType.Right;
263 case EVRButtonId.k_EButton_System:
265 offsetType = OffsetType.Right;
268 case Valve.VR.EVRButtonId.k_EButton_Grip:
270 offsetType = OffsetType.Forward;
273 case Valve.VR.EVRButtonId.k_EButton_SteamVR_Touchpad:
275 offsetType = OffsetType.Up;
281 switch ( offsetType )
283 case OffsetType.Forward:
284 hintInfo.textEndOffsetDir = hintInfo.localTransform.forward;
286 case OffsetType.Back:
287 hintInfo.textEndOffsetDir = -hintInfo.localTransform.forward;
289 case OffsetType.Right:
290 hintInfo.textEndOffsetDir = hintInfo.localTransform.right;
293 hintInfo.textEndOffsetDir = hintInfo.localTransform.up;
298 Vector3 hintStartPos = hintInfo.localTransform.position + ( hintInfo.localTransform.forward * 0.01f );
299 hintInfo.textHintObject = GameObject.Instantiate( textHintPrefab, hintStartPos, Quaternion.identity ) as GameObject;
300 hintInfo.textHintObject.name =
"Hint_" + hintInfo.componentName +
"_Start";
301 hintInfo.textHintObject.transform.SetParent( textHintParent );
302 hintInfo.textHintObject.layer = gameObject.layer;
303 hintInfo.textHintObject.tag = gameObject.tag;
306 hintInfo.textStartAnchor = hintInfo.textHintObject.transform.Find(
"Start" );
307 hintInfo.textEndAnchor = hintInfo.textHintObject.transform.Find(
"End" );
308 hintInfo.canvasOffset = hintInfo.textHintObject.transform.Find(
"CanvasOffset" );
309 hintInfo.line = hintInfo.textHintObject.transform.Find(
"Line" ).GetComponent<LineRenderer>();
310 hintInfo.textCanvas = hintInfo.textHintObject.GetComponentInChildren<Canvas>();
311 hintInfo.text = hintInfo.textCanvas.GetComponentInChildren<Text>();
312 hintInfo.textMesh = hintInfo.textCanvas.GetComponentInChildren<TextMesh>();
314 hintInfo.textHintObject.SetActive( false );
316 hintInfo.textStartAnchor.position = hintStartPos;
318 if ( hintInfo.text != null )
320 hintInfo.text.text = hintInfo.componentName;
323 if ( hintInfo.textMesh != null )
325 hintInfo.textMesh.text = hintInfo.componentName;
328 centerPosition += hintInfo.textStartAnchor.position;
331 hintInfo.textCanvas.transform.localScale = Vector3.Scale( hintInfo.textCanvas.transform.localScale, player.transform.localScale );
332 hintInfo.textStartAnchor.transform.localScale = Vector3.Scale( hintInfo.textStartAnchor.transform.localScale, player.transform.localScale );
333 hintInfo.textEndAnchor.transform.localScale = Vector3.Scale( hintInfo.textEndAnchor.transform.localScale, player.transform.localScale );
334 hintInfo.line.transform.localScale = Vector3.Scale( hintInfo.line.transform.localScale, player.transform.localScale );
339 private void ComputeTextEndTransforms()
344 centerPosition /= buttonHintInfos.Count;
345 float maxDistanceFromCenter = 0.0f;
347 foreach ( var hintInfo
in buttonHintInfos )
349 hintInfo.Value.distanceFromCenter = Vector3.Distance( hintInfo.Value.textStartAnchor.position, centerPosition );
351 if ( hintInfo.Value.distanceFromCenter > maxDistanceFromCenter )
353 maxDistanceFromCenter = hintInfo.Value.distanceFromCenter;
357 foreach ( var hintInfo
in buttonHintInfos )
359 Vector3 centerToButton = hintInfo.Value.textStartAnchor.position - centerPosition;
360 centerToButton.Normalize();
362 centerToButton = Vector3.Project( centerToButton, renderModel.transform.forward );
365 float t = hintInfo.Value.distanceFromCenter / maxDistanceFromCenter;
366 float scale = hintInfo.Value.distanceFromCenter * Mathf.Pow( 2, 10 * ( t - 1.0f ) ) * 20.0f;
369 float endPosOffset = 0.1f;
371 Vector3 hintEndPos = hintInfo.Value.textStartAnchor.position + ( hintInfo.Value.textEndOffsetDir * endPosOffset ) + ( centerToButton * scale * 0.1f );
372 hintInfo.Value.textEndAnchor.position = hintEndPos;
374 hintInfo.Value.canvasOffset.position = hintEndPos;
375 hintInfo.Value.canvasOffset.localRotation = Quaternion.identity;
381 private void ShowButtonHint( params EVRButtonId[] buttons )
383 renderModel.gameObject.SetActive( true );
385 renderModel.GetComponentsInChildren<MeshRenderer>( renderers );
386 for (
int i = 0; i < renderers.Count; i++ )
388 Texture mainTexture = renderers[i].material.mainTexture;
389 renderers[i].sharedMaterial = controllerMaterial;
390 renderers[i].material.mainTexture = mainTexture;
393 renderers[i].material.renderQueue = controllerMaterial.shader.renderQueue;
396 for (
int i = 0; i < buttons.Length; i++ )
398 if ( buttonHintInfos.ContainsKey( buttons[i] ) )
400 ButtonHintInfo hintInfo = buttonHintInfos[buttons[i]];
401 foreach ( MeshRenderer renderer
in hintInfo.renderers )
403 if ( !flashingRenderers.Contains( renderer ) )
405 flashingRenderers.Add( renderer );
411 startTime = Time.realtimeSinceStartup;
417 private void HideAllButtonHints()
421 renderModel.gameObject.SetActive( false );
426 private void HideButtonHint( params EVRButtonId[] buttons )
428 Color baseColor = controllerMaterial.GetColor( colorID );
429 for (
int i = 0; i < buttons.Length; i++ )
431 if ( buttonHintInfos.ContainsKey( buttons[i] ) )
433 ButtonHintInfo hintInfo = buttonHintInfos[buttons[i]];
434 foreach ( MeshRenderer renderer
in hintInfo.renderers )
436 renderer.material.color = baseColor;
437 flashingRenderers.Remove( renderer );
442 if ( flashingRenderers.Count == 0 )
444 renderModel.gameObject.SetActive( false );
452 private bool IsButtonHintActive( EVRButtonId button )
454 if ( buttonHintInfos.ContainsKey( button ) )
456 ButtonHintInfo hintInfo = buttonHintInfos[button];
457 foreach ( MeshRenderer buttonRenderer
in hintInfo.renderers )
459 if ( flashingRenderers.Contains( buttonRenderer ) )
471 private IEnumerator TestButtonHints()
475 ShowButtonHint( EVRButtonId.k_EButton_SteamVR_Trigger );
476 yield
return new WaitForSeconds( 1.0f );
477 ShowButtonHint( EVRButtonId.k_EButton_ApplicationMenu );
478 yield
return new WaitForSeconds( 1.0f );
479 ShowButtonHint( EVRButtonId.k_EButton_System );
480 yield
return new WaitForSeconds( 1.0f );
481 ShowButtonHint( EVRButtonId.k_EButton_Grip );
482 yield
return new WaitForSeconds( 1.0f );
483 ShowButtonHint( EVRButtonId.k_EButton_SteamVR_Touchpad );
484 yield
return new WaitForSeconds( 1.0f );
490 private IEnumerator TestTextHints()
494 ShowText( EVRButtonId.k_EButton_SteamVR_Trigger,
"Trigger" );
495 yield
return new WaitForSeconds( 3.0f );
496 ShowText( EVRButtonId.k_EButton_ApplicationMenu,
"Application" );
497 yield
return new WaitForSeconds( 3.0f );
498 ShowText( EVRButtonId.k_EButton_System,
"System" );
499 yield
return new WaitForSeconds( 3.0f );
500 ShowText( EVRButtonId.k_EButton_Grip,
"Grip" );
501 yield
return new WaitForSeconds( 3.0f );
502 ShowText( EVRButtonId.k_EButton_SteamVR_Touchpad,
"Touchpad" );
503 yield
return new WaitForSeconds( 3.0f );
506 yield
return new WaitForSeconds( 3.0f );
514 if ( renderModel != null && renderModel.gameObject.activeInHierarchy && flashingRenderers.Count > 0 )
516 Color baseColor = controllerMaterial.GetColor( colorID );
518 float flash = ( Time.realtimeSinceStartup - startTime ) * Mathf.PI * 2.0f;
519 flash = Mathf.Cos( flash );
520 flash = Util.RemapNumberClamped( flash, -1.0f, 1.0f, 0.0f, 1.0f );
522 float ticks = ( Time.realtimeSinceStartup - startTime );
523 if ( ticks - tickCount > 1.0f )
527 if ( device != null )
529 device.TriggerHapticPulse();
533 for (
int i = 0; i < flashingRenderers.Count; i++ )
535 Renderer r = flashingRenderers[i];
536 r.material.SetColor( colorID, Color.Lerp( baseColor, flashColor, flash ) );
541 foreach ( var hintInfo
in buttonHintInfos )
543 if ( hintInfo.Value.textHintActive )
545 UpdateTextHint( hintInfo.Value );
554 private void UpdateTextHint( ButtonHintInfo hintInfo )
556 Transform playerTransform = player.hmdTransform;
557 Vector3 vDir = playerTransform.position - hintInfo.canvasOffset.position;
559 Quaternion standardLookat = Quaternion.LookRotation( vDir, Vector3.up );
560 Quaternion upsideDownLookat = Quaternion.LookRotation( vDir, playerTransform.up );
563 if ( playerTransform.forward.y > 0.0f )
565 flInterp = Util.RemapNumberClamped( playerTransform.forward.y, 0.6f, 0.4f, 1.0f, 0.0f );
569 flInterp = Util.RemapNumberClamped( playerTransform.forward.y, -0.8f, -0.6f, 1.0f, 0.0f );
572 hintInfo.canvasOffset.rotation = Quaternion.Slerp( standardLookat, upsideDownLookat, flInterp );
574 Transform lineTransform = hintInfo.line.transform;
576 hintInfo.line.useWorldSpace =
false;
577 hintInfo.line.SetPosition( 0, lineTransform.InverseTransformPoint( hintInfo.textStartAnchor.position ) );
578 hintInfo.line.SetPosition( 1, lineTransform.InverseTransformPoint( hintInfo.textEndAnchor.position ) );
586 flashingRenderers.Clear();
591 private void ShowText( EVRButtonId button,
string text,
bool highlightButton =
true )
593 if ( buttonHintInfos.ContainsKey( button ) )
595 ButtonHintInfo hintInfo = buttonHintInfos[button];
596 hintInfo.textHintObject.SetActive( true );
597 hintInfo.textHintActive =
true;
599 if ( hintInfo.text != null )
601 hintInfo.text.text = text;
604 if ( hintInfo.textMesh != null )
606 hintInfo.textMesh.text = text;
609 UpdateTextHint( hintInfo );
611 if ( highlightButton )
613 ShowButtonHint( button );
616 renderModel.gameObject.SetActive( true );
622 private void HideText( EVRButtonId button )
624 if ( buttonHintInfos.ContainsKey( button ) )
626 ButtonHintInfo hintInfo = buttonHintInfos[button];
627 hintInfo.textHintObject.SetActive( false );
628 hintInfo.textHintActive =
false;
630 HideButtonHint( button );
636 private void HideAllText()
638 foreach ( var hintInfo
in buttonHintInfos )
640 hintInfo.Value.textHintObject.SetActive( false );
641 hintInfo.Value.textHintActive =
false;
644 HideAllButtonHints();
649 private string GetActiveHintText( EVRButtonId button )
651 if ( buttonHintInfos.ContainsKey( button ) )
653 ButtonHintInfo hintInfo = buttonHintInfos[button];
654 if ( hintInfo.textHintActive )
656 return hintInfo.text.text;
673 if ( hints != null && hints.initialized )
684 public static void ShowButtonHint(
Hand hand, params EVRButtonId[] buttons )
689 hints.ShowButtonHint( buttons );
695 public static void HideButtonHint(
Hand hand, params EVRButtonId[] buttons )
700 hints.HideButtonHint( buttons );
706 public static void HideAllButtonHints(
Hand hand )
711 hints.HideAllButtonHints();
717 public static bool IsButtonHintActive(
Hand hand, EVRButtonId button )
722 return hints.IsButtonHintActive( button );
730 public static void ShowTextHint(
Hand hand, EVRButtonId button,
string text,
bool highlightButton =
true )
735 hints.ShowText( button, text, highlightButton );
741 public static void HideTextHint(
Hand hand, EVRButtonId button )
746 hints.HideText( button );
752 public static void HideAllTextHints(
Hand hand )
763 public static string GetActiveHintText(
Hand hand, EVRButtonId button )
768 return hints.GetActiveHintText( button );