1 /** 2 AR/VR interface using WebXR. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.webxrinterface; 14 import std.meta : AliasSeq, staticIndexOf; 15 import std.traits : Unqual; 16 import godot.d.traits; 17 import godot.core; 18 import godot.c; 19 import godot.d.bind; 20 import godot.d.reference; 21 import godot.globalenums; 22 import godot.object; 23 import godot.arvrinterface; 24 import godot.reference; 25 import godot.arvrpositionaltracker; 26 /** 27 AR/VR interface using WebXR. 28 29 WebXR is an open standard that allows creating VR and AR applications that run in the web browser. 30 As such, this interface is only available when running in an HTML5 export. 31 WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones). 32 Since WebXR is based on Javascript, it makes extensive use of callbacks, which means that $(D WebXRInterface) is forced to use signals, where other AR/VR interfaces would instead use functions that return a result immediately. This makes $(D WebXRInterface) quite a bit more complicated to initialize than other AR/VR interfaces. 33 Here's the minimum code required to start an immersive VR session: 34 35 36 extends Spatial 37 38 var webxr_interface 39 var vr_supported = false 40 41 func _ready(): 42 # We assume this node has a button as a child. 43 # This button is for the user to consent to entering immersive VR mode. 44 $Button.connect("pressed", self, "_on_Button_pressed") 45 46 webxr_interface = ARVRServer.find_interface("WebXR") 47 if webxr_interface: 48 # WebXR uses a lot of asynchronous callbacks, so we connect to various 49 # signals in order to receive them. 50 webxr_interface.connect("session_supported", self, "_webxr_session_supported") 51 webxr_interface.connect("session_started", self, "_webxr_session_started") 52 webxr_interface.connect("session_ended", self, "_webxr_session_ended") 53 webxr_interface.connect("session_failed", self, "_webxr_session_failed") 54 55 # This returns immediately - our _webxr_session_supported() method 56 # (which we connected to the "session_supported" signal above) will 57 # be called sometime later to let us know if it's supported or not. 58 webxr_interface.is_session_supported("immersive-vr") 59 60 func _webxr_session_supported(session_mode, supported): 61 if session_mode == 'immersive-vr': 62 vr_supported = supported 63 64 func _on_Button_pressed(): 65 if not vr_supported: 66 OS.alert("Your browser doesn't support VR") 67 return 68 69 # We want an immersive VR session, as opposed to AR ('immersive-ar') or a 70 # simple 3DoF viewer ('viewer'). 71 webxr_interface.session_mode = 'immersive-vr' 72 # 'bounded-floor' is room scale, 'local-floor' is a standing or sitting 73 # experience (it puts you 1.6m above the ground if you have 3DoF headset), 74 # whereas as 'local' puts you down at the ARVROrigin. 75 # This list means it'll first try to request 'bounded-floor', then 76 # fallback on 'local-floor' and ultimately 'local', if nothing else is 77 # supported. 78 webxr_interface.requested_reference_space_types = 'bounded-floor, local-floor, local' 79 # In order to use 'local-floor' or 'bounded-floor' we must also 80 # mark the features as required or optional. 81 webxr_interface.required_features = 'local-floor' 82 webxr_interface.optional_features = 'bounded-floor' 83 84 # This will return false if we're unable to even request the session, 85 # however, it can still fail asynchronously later in the process, so we 86 # only know if it's really succeeded or failed when our 87 # _webxr_session_started() or _webxr_session_failed() methods are called. 88 if not webxr_interface.initialize(): 89 OS.alert("Failed to initialize") 90 return 91 92 func _webxr_session_started(): 93 $Button.visible = false 94 # This tells Godot to start rendering to the headset. 95 get_viewport().arvr = true 96 # This will be the reference space type you ultimately got, out of the 97 # types that you requested above. This is useful if you want the game to 98 # work a little differently in 'bounded-floor' versus 'local-floor'. 99 print ("Reference space type: " + webxr_interface.reference_space_type) 100 101 func _webxr_session_ended(): 102 $Button.visible = true 103 # If the user exits immersive mode, then we tell Godot to render to the web 104 # page again. 105 get_viewport().arvr = false 106 107 func _webxr_session_failed(message): 108 OS.alert("Failed to initialize: " + message) 109 110 111 There are several ways to handle "controller" input: 112 - Using $(D ARVRController) nodes and their $(D ARVRController.buttonPressed) and $(D ARVRController.buttonRelease) signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by $(D url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping)Section 3.3 of the WebXR Gamepads Module$(D /url). 113 - Using $(D Node._unhandledInput) and $(D InputEventJoypadButton) or $(D InputEventJoypadMotion). This works the same as normal joypads, except the $(D InputEvent.device) starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by $(D url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping)Section 3.3 of the WebXR Gamepads Module$(D /url). 114 - Using the $(D select), $(D squeeze) and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself. The `controller_id` passed to these signals is the same id as used in $(D ARVRController.controllerId). 115 You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interations with more advanced devices. 116 */ 117 @GodotBaseClass struct WebXRInterface 118 { 119 package(godot) enum string _GODOT_internal_name = "WebXRInterface"; 120 public: 121 @nogc nothrow: 122 union { /** */ godot_object _godot_object; /** */ ARVRInterface _GODOT_base; } 123 alias _GODOT_base this; 124 alias BaseClasses = AliasSeq!(typeof(_GODOT_base), typeof(_GODOT_base).BaseClasses); 125 package(godot) __gshared bool _classBindingInitialized = false; 126 package(godot) static struct GDNativeClassBinding 127 { 128 __gshared: 129 @GodotName("get_bounds_geometry") GodotMethod!(PoolVector3Array) getBoundsGeometry; 130 @GodotName("get_controller") GodotMethod!(ARVRPositionalTracker, long) getController; 131 @GodotName("get_optional_features") GodotMethod!(String) getOptionalFeatures; 132 @GodotName("get_reference_space_type") GodotMethod!(String) getReferenceSpaceType; 133 @GodotName("get_requested_reference_space_types") GodotMethod!(String) getRequestedReferenceSpaceTypes; 134 @GodotName("get_required_features") GodotMethod!(String) getRequiredFeatures; 135 @GodotName("get_session_mode") GodotMethod!(String) getSessionMode; 136 @GodotName("get_visibility_state") GodotMethod!(String) getVisibilityState; 137 @GodotName("is_session_supported") GodotMethod!(void, String) isSessionSupported; 138 @GodotName("set_optional_features") GodotMethod!(void, String) setOptionalFeatures; 139 @GodotName("set_requested_reference_space_types") GodotMethod!(void, String) setRequestedReferenceSpaceTypes; 140 @GodotName("set_required_features") GodotMethod!(void, String) setRequiredFeatures; 141 @GodotName("set_session_mode") GodotMethod!(void, String) setSessionMode; 142 } 143 /// 144 pragma(inline, true) bool opEquals(in WebXRInterface other) const 145 { return _godot_object.ptr is other._godot_object.ptr; } 146 /// 147 pragma(inline, true) typeof(null) opAssign(typeof(null) n) 148 { _godot_object.ptr = n; return null; } 149 /// 150 pragma(inline, true) bool opEquals(typeof(null) n) const 151 { return _godot_object.ptr is n; } 152 /// 153 size_t toHash() const @trusted { return cast(size_t)_godot_object.ptr; } 154 mixin baseCasts; 155 /// Construct a new instance of WebXRInterface. 156 /// Note: use `memnew!WebXRInterface` instead. 157 static WebXRInterface _new() 158 { 159 static godot_class_constructor constructor; 160 if(constructor is null) constructor = _godot_api.godot_get_class_constructor("WebXRInterface"); 161 if(constructor is null) return typeof(this).init; 162 return cast(WebXRInterface)(constructor()); 163 } 164 @disable new(size_t s); 165 /** 166 167 */ 168 PoolVector3Array getBoundsGeometry() const 169 { 170 checkClassBinding!(typeof(this))(); 171 return ptrcall!(PoolVector3Array)(GDNativeClassBinding.getBoundsGeometry, _godot_object); 172 } 173 /** 174 Gets an $(D ARVRPositionalTracker) for the given `controller_id`. 175 In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the $(D ARVRPositionalTracker) as a ray pointing at the object the user wishes to interact with. 176 Use this method to get information about the controller that triggered one of these signals: 177 - $(D selectstart) 178 - $(D select) 179 - $(D selectend) 180 - $(D squeezestart) 181 - $(D squeeze) 182 - $(D squeezestart) 183 */ 184 ARVRPositionalTracker getController(in long controller_id) const 185 { 186 checkClassBinding!(typeof(this))(); 187 return ptrcall!(ARVRPositionalTracker)(GDNativeClassBinding.getController, _godot_object, controller_id); 188 } 189 /** 190 191 */ 192 String getOptionalFeatures() const 193 { 194 checkClassBinding!(typeof(this))(); 195 return ptrcall!(String)(GDNativeClassBinding.getOptionalFeatures, _godot_object); 196 } 197 /** 198 199 */ 200 String getReferenceSpaceType() const 201 { 202 checkClassBinding!(typeof(this))(); 203 return ptrcall!(String)(GDNativeClassBinding.getReferenceSpaceType, _godot_object); 204 } 205 /** 206 207 */ 208 String getRequestedReferenceSpaceTypes() const 209 { 210 checkClassBinding!(typeof(this))(); 211 return ptrcall!(String)(GDNativeClassBinding.getRequestedReferenceSpaceTypes, _godot_object); 212 } 213 /** 214 215 */ 216 String getRequiredFeatures() const 217 { 218 checkClassBinding!(typeof(this))(); 219 return ptrcall!(String)(GDNativeClassBinding.getRequiredFeatures, _godot_object); 220 } 221 /** 222 223 */ 224 String getSessionMode() const 225 { 226 checkClassBinding!(typeof(this))(); 227 return ptrcall!(String)(GDNativeClassBinding.getSessionMode, _godot_object); 228 } 229 /** 230 231 */ 232 String getVisibilityState() const 233 { 234 checkClassBinding!(typeof(this))(); 235 return ptrcall!(String)(GDNativeClassBinding.getVisibilityState, _godot_object); 236 } 237 /** 238 Checks if the given `session_mode` is supported by the user's browser. 239 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode)WebXR's XRSessionMode$(D /url), including: `"immersive-vr"`, `"immersive-ar"`, and `"inline"`. 240 This method returns nothing, instead it emits the $(D sessionSupported) signal with the result. 241 */ 242 void isSessionSupported(in String session_mode) 243 { 244 checkClassBinding!(typeof(this))(); 245 ptrcall!(void)(GDNativeClassBinding.isSessionSupported, _godot_object, session_mode); 246 } 247 /** 248 249 */ 250 void setOptionalFeatures(in String optional_features) 251 { 252 checkClassBinding!(typeof(this))(); 253 ptrcall!(void)(GDNativeClassBinding.setOptionalFeatures, _godot_object, optional_features); 254 } 255 /** 256 257 */ 258 void setRequestedReferenceSpaceTypes(in String requested_reference_space_types) 259 { 260 checkClassBinding!(typeof(this))(); 261 ptrcall!(void)(GDNativeClassBinding.setRequestedReferenceSpaceTypes, _godot_object, requested_reference_space_types); 262 } 263 /** 264 265 */ 266 void setRequiredFeatures(in String required_features) 267 { 268 checkClassBinding!(typeof(this))(); 269 ptrcall!(void)(GDNativeClassBinding.setRequiredFeatures, _godot_object, required_features); 270 } 271 /** 272 273 */ 274 void setSessionMode(in String session_mode) 275 { 276 checkClassBinding!(typeof(this))(); 277 ptrcall!(void)(GDNativeClassBinding.setSessionMode, _godot_object, session_mode); 278 } 279 /** 280 The vertices of a polygon which defines the boundaries of the user's play area. 281 This will only be available if $(D referenceSpaceType) is `"bounded-floor"` and only on certain browsers and devices that support it. 282 The $(D referenceSpaceReset) signal may indicate when this changes. 283 */ 284 @property PoolVector3Array boundsGeometry() 285 { 286 return getBoundsGeometry(); 287 } 288 /** 289 A comma-seperated list of optional features used by $(D ARVRInterface.initialize) when setting up the WebXR session. 290 If a user's browser or device doesn't support one of the given features, initialization will continue, but you won't be able to use the requested feature. 291 This doesn't have any effect on the interface when already initialized. 292 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType)WebXR's XRReferenceSpaceType$(D /url). If you want to use a particular reference space type, it must be listed in either $(D requiredFeatures) or $(D optionalFeatures). 293 */ 294 @property String optionalFeatures() 295 { 296 return getOptionalFeatures(); 297 } 298 /// ditto 299 @property void optionalFeatures(String v) 300 { 301 setOptionalFeatures(v); 302 } 303 /** 304 The reference space type (from the list of requested types set in the $(D requestedReferenceSpaceTypes) property), that was ultimately used by $(D ARVRInterface.initialize) when setting up the WebXR session. 305 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType)WebXR's XRReferenceSpaceType$(D /url). If you want to use a particular reference space type, it must be listed in either $(D requiredFeatures) or $(D optionalFeatures). 306 */ 307 @property String referenceSpaceType() 308 { 309 return getReferenceSpaceType(); 310 } 311 /** 312 A comma-seperated list of reference space types used by $(D ARVRInterface.initialize) when setting up the WebXR session. 313 The reference space types are requested in order, and the first on supported by the users device or browser will be used. The $(D referenceSpaceType) property contains the reference space type that was ultimately used. 314 This doesn't have any effect on the interface when already initialized. 315 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType)WebXR's XRReferenceSpaceType$(D /url). If you want to use a particular reference space type, it must be listed in either $(D requiredFeatures) or $(D optionalFeatures). 316 */ 317 @property String requestedReferenceSpaceTypes() 318 { 319 return getRequestedReferenceSpaceTypes(); 320 } 321 /// ditto 322 @property void requestedReferenceSpaceTypes(String v) 323 { 324 setRequestedReferenceSpaceTypes(v); 325 } 326 /** 327 A comma-seperated list of required features used by $(D ARVRInterface.initialize) when setting up the WebXR session. 328 If a user's browser or device doesn't support one of the given features, initialization will fail and $(D sessionFailed) will be emitted. 329 This doesn't have any effect on the interface when already initialized. 330 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType)WebXR's XRReferenceSpaceType$(D /url). If you want to use a particular reference space type, it must be listed in either $(D requiredFeatures) or $(D optionalFeatures). 331 */ 332 @property String requiredFeatures() 333 { 334 return getRequiredFeatures(); 335 } 336 /// ditto 337 @property void requiredFeatures(String v) 338 { 339 setRequiredFeatures(v); 340 } 341 /** 342 The session mode used by $(D ARVRInterface.initialize) when setting up the WebXR session. 343 This doesn't have any effect on the interface when already initialized. 344 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode)WebXR's XRSessionMode$(D /url), including: `"immersive-vr"`, `"immersive-ar"`, and `"inline"`. 345 */ 346 @property String sessionMode() 347 { 348 return getSessionMode(); 349 } 350 /// ditto 351 @property void sessionMode(String v) 352 { 353 setSessionMode(v); 354 } 355 /** 356 Indicates if the WebXR session's imagery is visible to the user. 357 Possible values come from $(D url=https://developer.mozilla.org/en-US/docs/Web/API/XRVisibilityState)WebXR's XRVisibilityState$(D /url), including `"hidden"`, `"visible"`, and `"visible-blurred"`. 358 */ 359 @property String visibilityState() 360 { 361 return getVisibilityState(); 362 } 363 }