start_vr.gd 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. extends Node3D
  2. signal focus_lost
  3. signal focus_gained
  4. signal pose_recentered
  5. @export var maximum_refresh_rate : int = 90
  6. var xr_interface : OpenXRInterface
  7. var xr_is_focused := false
  8. # Called when the node enters the scene tree for the first time.
  9. func _ready() -> void:
  10. xr_interface = XRServer.find_interface("OpenXR")
  11. if xr_interface and xr_interface.is_initialized():
  12. print("OpenXR instantiated successfully.")
  13. var vp : Viewport = get_viewport()
  14. # Enable XR on our viewport.
  15. vp.use_xr = true
  16. # Make sure V-Sync is off, as V-Sync is handled by OpenXR.
  17. DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
  18. # Enable variable rate shading.
  19. if RenderingServer.get_rendering_device():
  20. vp.vrs_mode = Viewport.VRS_XR
  21. elif int(ProjectSettings.get_setting("xr/openxr/foveation_level")) == 0:
  22. push_warning("OpenXR: Recommend setting Foveation level to High in Project Settings")
  23. # Connect the OpenXR events.
  24. xr_interface.session_begun.connect(_on_openxr_session_begun)
  25. xr_interface.session_visible.connect(_on_openxr_visible_state)
  26. xr_interface.session_focussed.connect(_on_openxr_focused_state)
  27. xr_interface.session_stopping.connect(_on_openxr_stopping)
  28. xr_interface.pose_recentered.connect(_on_openxr_pose_recentered)
  29. else:
  30. # We couldn't start OpenXR.
  31. print("OpenXR not instantiated!")
  32. get_tree().quit()
  33. # Handle OpenXR session ready.
  34. func _on_openxr_session_begun() -> void:
  35. # Get the reported refresh rate.
  36. var current_refresh_rate := xr_interface.get_display_refresh_rate()
  37. if current_refresh_rate > 0:
  38. print("OpenXR: Refresh rate reported as ", str(current_refresh_rate))
  39. else:
  40. print("OpenXR: No refresh rate given by XR runtime")
  41. # See if we have a better refresh rate available.
  42. var new_rate := current_refresh_rate
  43. var available_rates: Array[float]
  44. available_rates.assign(xr_interface.get_available_display_refresh_rates())
  45. if available_rates.is_empty():
  46. print("OpenXR: Target does not support refresh rate extension")
  47. elif available_rates.size() == 1:
  48. # Only one available, so use it.
  49. new_rate = available_rates[0]
  50. else:
  51. for rate in available_rates:
  52. if rate > new_rate and rate <= maximum_refresh_rate:
  53. new_rate = rate
  54. # Did we find a better rate?
  55. if current_refresh_rate != new_rate:
  56. print("OpenXR: Setting refresh rate to ", str(new_rate))
  57. xr_interface.set_display_refresh_rate(new_rate)
  58. current_refresh_rate = new_rate
  59. # Now match our physics rate. This is currently needed to avoid jittering,
  60. # due to physics interpolation not being used.
  61. Engine.physics_ticks_per_second = roundi(current_refresh_rate)
  62. # Handle OpenXR visible state.
  63. func _on_openxr_visible_state() -> void:
  64. # We always pass this state at startup,
  65. # but the second time we get this, it means our player took off their headset.
  66. if xr_is_focused:
  67. print("OpenXR lost focus")
  68. xr_is_focused = false
  69. # Pause our game.
  70. process_mode = Node.PROCESS_MODE_DISABLED
  71. focus_lost.emit()
  72. # Handle OpenXR focused state
  73. func _on_openxr_focused_state() -> void:
  74. print("OpenXR gained focus")
  75. xr_is_focused = true
  76. # Unpause our game.
  77. process_mode = Node.PROCESS_MODE_INHERIT
  78. focus_gained.emit()
  79. # Handle OpenXR stopping state.
  80. func _on_openxr_stopping() -> void:
  81. # Our session is being stopped.
  82. print("OpenXR is stopping")
  83. # Handle OpenXR pose recentered signal.
  84. func _on_openxr_pose_recentered() -> void:
  85. # User recentered view, we have to react to this by recentering the view.
  86. # This is game implementation dependent.
  87. pose_recentered.emit()