2020-09-26 17:15:21 -05:00
/**************************************************************************/
/* library_godot_webxr.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2023-01-05 13:25:55 +01:00
2020-09-26 17:15:21 -05:00
const GodotWebXR = {
2025-06-12 13:12:46 -04:00
$GodotWebXR _ _deps : [ '$MainLoop' , '$GL' , '$GodotRuntime' , '$runtimeKeepalivePush' , '$runtimeKeepalivePop' ] ,
2020-09-26 17:15:21 -05:00
$GodotWebXR : {
gl : null ,
session : null ,
2022-11-18 20:43:11 -06:00
gl _binding : null ,
layer : null ,
2020-09-26 17:15:21 -05:00
space : null ,
frame : null ,
pose : null ,
2022-11-18 20:43:11 -06:00
view _count : 1 ,
input _sources : new Array ( 16 ) ,
touches : new Array ( 5 ) ,
2023-02-08 21:02:13 -06:00
onsimpleevent : null ,
2020-09-26 17:15:21 -05:00
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
// when an XR session is started.
orig _requestAnimationFrame : null ,
requestAnimationFrame : ( callback ) => {
if ( GodotWebXR . session && GodotWebXR . space ) {
const onFrame = function ( time , frame ) {
GodotWebXR . frame = frame ;
GodotWebXR . pose = frame . getViewerPose ( GodotWebXR . space ) ;
callback ( time ) ;
GodotWebXR . frame = null ;
GodotWebXR . pose = null ;
} ;
GodotWebXR . session . requestAnimationFrame ( onFrame ) ;
} else {
GodotWebXR . orig _requestAnimationFrame ( callback ) ;
}
} ,
monkeyPatchRequestAnimationFrame : ( enable ) => {
if ( GodotWebXR . orig _requestAnimationFrame === null ) {
2025-06-12 13:12:46 -04:00
GodotWebXR . orig _requestAnimationFrame = MainLoop . requestAnimationFrame ;
2020-09-26 17:15:21 -05:00
}
2025-06-12 13:12:46 -04:00
MainLoop . requestAnimationFrame = enable
2024-05-12 04:16:02 +02:00
? GodotWebXR . requestAnimationFrame
: GodotWebXR . orig _requestAnimationFrame ;
2020-09-26 17:15:21 -05:00
} ,
pauseResumeMainLoop : ( ) => {
// Once both GodotWebXR.session and GodotWebXR.space are set or
// unset, our monkey-patched requestAnimationFrame() should be
// enabled or disabled. When using the WebXR API Emulator, this
// gets picked up automatically, however, in the Oculus Browser
// on the Quest, we need to pause and resume the main loop.
2025-06-12 13:12:46 -04:00
MainLoop . pause ( ) ;
2024-05-12 04:16:02 +02:00
runtimeKeepalivePush ( ) ;
2020-09-26 17:15:21 -05:00
window . setTimeout ( function ( ) {
2024-05-12 04:16:02 +02:00
runtimeKeepalivePop ( ) ;
2025-06-12 13:12:46 -04:00
MainLoop . resume ( ) ;
2020-09-26 17:15:21 -05:00
} , 0 ) ;
} ,
2022-11-18 20:43:11 -06:00
getLayer : ( ) => {
const new _view _count = ( GodotWebXR . pose ) ? GodotWebXR . pose . views . length : 1 ;
let layer = GodotWebXR . layer ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
// If the view count hasn't changed since creating this layer, then
// we can simply return it.
if ( layer && GodotWebXR . view _count === new _view _count ) {
return layer ;
2020-09-26 17:15:21 -05:00
}
2025-06-08 12:25:42 -05:00
if ( ! GodotWebXR . session || ! GodotWebXR . gl _binding || ! GodotWebXR . gl _binding . createProjectionLayer ) {
2022-11-18 20:43:11 -06:00
return null ;
}
const gl = GodotWebXR . gl ;
layer = GodotWebXR . gl _binding . createProjectionLayer ( {
textureType : new _view _count > 1 ? 'texture-array' : 'texture' ,
colorFormat : gl . RGBA8 ,
depthFormat : gl . DEPTH _COMPONENT24 ,
} ) ;
GodotWebXR . session . updateRenderState ( { layers : [ layer ] } ) ;
GodotWebXR . layer = layer ;
GodotWebXR . view _count = new _view _count ;
return layer ;
} ,
getSubImage : ( ) => {
if ( ! GodotWebXR . pose ) {
return null ;
}
const layer = GodotWebXR . getLayer ( ) ;
if ( layer === null ) {
return null ;
}
// Because we always use "texture-array" for multiview and "texture"
// when there is only 1 view, it should be safe to only grab the
// subimage for the first view.
return GodotWebXR . gl _binding . getViewSubImage ( layer , GodotWebXR . pose . views [ 0 ] ) ;
} ,
getTextureId : ( texture ) => {
if ( texture . name !== undefined ) {
return texture . name ;
}
const id = GL . getNewId ( GL . textures ) ;
texture . name = id ;
GL . textures [ id ] = texture ;
return id ;
} ,
addInputSource : ( input _source ) => {
let name = - 1 ;
if ( input _source . targetRayMode === 'tracked-pointer' && input _source . handedness === 'left' ) {
name = 0 ;
} else if ( input _source . targetRayMode === 'tracked-pointer' && input _source . handedness === 'right' ) {
name = 1 ;
} else {
for ( let i = 2 ; i < 16 ; i ++ ) {
if ( ! GodotWebXR . input _sources [ i ] ) {
name = i ;
break ;
2020-09-26 17:15:21 -05:00
}
}
2022-11-18 20:43:11 -06:00
}
if ( name >= 0 ) {
GodotWebXR . input _sources [ name ] = input _source ;
input _source . name = name ;
// Find a free touch index for screen sources.
if ( input _source . targetRayMode === 'screen' ) {
let touch _index = - 1 ;
for ( let i = 0 ; i < 5 ; i ++ ) {
if ( ! GodotWebXR . touches [ i ] ) {
touch _index = i ;
break ;
}
}
if ( touch _index >= 0 ) {
GodotWebXR . touches [ touch _index ] = input _source ;
input _source . touch _index = touch _index ;
}
}
}
return name ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
removeInputSource : ( input _source ) => {
if ( input _source . name !== undefined ) {
const name = input _source . name ;
if ( name >= 0 && name < 16 ) {
GodotWebXR . input _sources [ name ] = null ;
}
if ( input _source . touch _index !== undefined ) {
const touch _index = input _source . touch _index ;
if ( touch _index >= 0 && touch _index < 5 ) {
GodotWebXR . touches [ touch _index ] = null ;
}
}
return name ;
}
return - 1 ;
} ,
getInputSourceId : ( input _source ) => {
if ( input _source !== undefined ) {
return input _source . name ;
}
return - 1 ;
} ,
getTouchIndex : ( input _source ) => {
if ( input _source . touch _index !== undefined ) {
return input _source . touch _index ;
}
return - 1 ;
} ,
2020-09-26 17:15:21 -05:00
} ,
godot _webxr _is _supported _ _proxy : 'sync' ,
godot _webxr _is _supported _ _sig : 'i' ,
godot _webxr _is _supported : function ( ) {
return ! ! navigator . xr ;
} ,
godot _webxr _is _session _supported _ _proxy : 'sync' ,
godot _webxr _is _session _supported _ _sig : 'vii' ,
godot _webxr _is _session _supported : function ( p _session _mode , p _callback ) {
const session _mode = GodotRuntime . parseString ( p _session _mode ) ;
const cb = GodotRuntime . get _func ( p _callback ) ;
if ( navigator . xr ) {
navigator . xr . isSessionSupported ( session _mode ) . then ( function ( supported ) {
const c _str = GodotRuntime . allocString ( session _mode ) ;
cb ( c _str , supported ? 1 : 0 ) ;
GodotRuntime . free ( c _str ) ;
} ) ;
} else {
const c _str = GodotRuntime . allocString ( session _mode ) ;
cb ( c _str , 0 ) ;
GodotRuntime . free ( c _str ) ;
}
} ,
godot _webxr _initialize _ _deps : [ 'emscripten_webgl_get_current_context' ] ,
godot _webxr _initialize _ _proxy : 'sync' ,
2022-11-18 20:43:11 -06:00
godot _webxr _initialize _ _sig : 'viiiiiiiii' ,
godot _webxr _initialize : function ( p _session _mode , p _required _features , p _optional _features , p _requested _reference _spaces , p _on _session _started , p _on _session _ended , p _on _session _failed , p _on _input _event , p _on _simple _event ) {
2020-09-26 17:15:21 -05:00
GodotWebXR . monkeyPatchRequestAnimationFrame ( true ) ;
const session _mode = GodotRuntime . parseString ( p _session _mode ) ;
const required _features = GodotRuntime . parseString ( p _required _features ) . split ( ',' ) . map ( ( s ) => s . trim ( ) ) . filter ( ( s ) => s !== '' ) ;
const optional _features = GodotRuntime . parseString ( p _optional _features ) . split ( ',' ) . map ( ( s ) => s . trim ( ) ) . filter ( ( s ) => s !== '' ) ;
const requested _reference _space _types = GodotRuntime . parseString ( p _requested _reference _spaces ) . split ( ',' ) . map ( ( s ) => s . trim ( ) ) ;
const onstarted = GodotRuntime . get _func ( p _on _session _started ) ;
const onended = GodotRuntime . get _func ( p _on _session _ended ) ;
const onfailed = GodotRuntime . get _func ( p _on _session _failed ) ;
const oninputevent = GodotRuntime . get _func ( p _on _input _event ) ;
const onsimpleevent = GodotRuntime . get _func ( p _on _simple _event ) ;
const session _init = { } ;
if ( required _features . length > 0 ) {
session _init [ 'requiredFeatures' ] = required _features ;
}
if ( optional _features . length > 0 ) {
session _init [ 'optionalFeatures' ] = optional _features ;
}
navigator . xr . requestSession ( session _mode , session _init ) . then ( function ( session ) {
GodotWebXR . session = session ;
session . addEventListener ( 'end' , function ( evt ) {
onended ( ) ;
} ) ;
session . addEventListener ( 'inputsourceschange' , function ( evt ) {
2022-11-18 20:43:11 -06:00
evt . added . forEach ( GodotWebXR . addInputSource ) ;
evt . removed . forEach ( GodotWebXR . removeInputSource ) ;
2020-09-26 17:15:21 -05:00
} ) ;
2022-11-18 20:43:11 -06:00
[ 'selectstart' , 'selectend' , 'squeezestart' , 'squeezeend' ] . forEach ( ( input _event , index ) => {
2020-09-26 17:15:21 -05:00
session . addEventListener ( input _event , function ( evt ) {
2022-11-18 20:43:11 -06:00
// Since this happens in-between normal frames, we need to
// grab the frame from the event in order to get poses for
// the input sources.
GodotWebXR . frame = evt . frame ;
oninputevent ( index , GodotWebXR . getInputSourceId ( evt . inputSource ) ) ;
GodotWebXR . frame = null ;
2020-09-26 17:15:21 -05:00
} ) ;
} ) ;
session . addEventListener ( 'visibilitychange' , function ( evt ) {
const c _str = GodotRuntime . allocString ( 'visibility_state_changed' ) ;
onsimpleevent ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} ) ;
2023-02-08 21:02:13 -06:00
// Store onsimpleevent so we can use it later.
GodotWebXR . onsimpleevent = onsimpleevent ;
2024-05-12 04:16:02 +02:00
const gl _context _handle = _emscripten _webgl _get _current _context ( ) ;
2020-09-26 17:15:21 -05:00
const gl = GL . getContext ( gl _context _handle ) . GLctx ;
GodotWebXR . gl = gl ;
gl . makeXRCompatible ( ) . then ( function ( ) {
2025-06-08 12:25:42 -05:00
const throwNoWebXRLayersError = ( ) => {
throw new Error ( 'This browser doesn\'t support WebXR Layers (which Godot requires) nor is the polyfill in use. If you are the developer of this application, please consider including the polyfill.' ) ;
} ;
try {
GodotWebXR . gl _binding = new XRWebGLBinding ( session , gl ) ;
} catch ( error ) {
// We'll end up here for browsers that don't have XRWebGLBinding at all, or if the browser does support WebXR Layers,
// but is using the WebXR polyfill, so calling native XRWebGLBinding with the polyfilled XRSession won't work.
throwNoWebXRLayersError ( ) ;
}
if ( ! GodotWebXR . gl _binding . createProjectionLayer ) {
// On other browsers, XRWebGLBinding exists and works, but it doesn't support creating projection layers (which is
// contrary to the spec, which says this MUST be supported) and so the polyfill is required.
throwNoWebXRLayersError ( ) ;
}
2022-11-18 20:43:11 -06:00
// This will trigger the layer to get created.
2025-06-08 12:25:42 -05:00
const layer = GodotWebXR . getLayer ( ) ;
if ( ! layer ) {
throw new Error ( 'Unable to create WebXR Layer.' ) ;
}
2020-09-26 17:15:21 -05:00
function onReferenceSpaceSuccess ( reference _space , reference _space _type ) {
GodotWebXR . space = reference _space ;
// Using reference_space.addEventListener() crashes when
// using the polyfill with the WebXR Emulator extension,
// so we set the event property instead.
reference _space . onreset = function ( evt ) {
const c _str = GodotRuntime . allocString ( 'reference_space_reset' ) ;
onsimpleevent ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} ;
// Now that both GodotWebXR.session and GodotWebXR.space are
// set, we need to pause and resume the main loop for the XR
// main loop to kick in.
GodotWebXR . pauseResumeMainLoop ( ) ;
// Call in setTimeout() so that errors in the onstarted()
// callback don't bubble up here and cause Godot to try the
// next reference space.
window . setTimeout ( function ( ) {
2024-02-15 14:58:38 -06:00
const reference _space _c _str = GodotRuntime . allocString ( reference _space _type ) ;
2024-06-25 08:47:54 -05:00
const enabled _features = 'enabledFeatures' in session ? Array . from ( session . enabledFeatures ) : [ ] ;
const enabled _features _c _str = GodotRuntime . allocString ( enabled _features . join ( ',' ) ) ;
2024-06-26 21:40:07 -05:00
const environment _blend _mode = 'environmentBlendMode' in session ? session . environmentBlendMode : '' ;
const environment _blend _mode _c _str = GodotRuntime . allocString ( environment _blend _mode ) ;
onstarted ( reference _space _c _str , enabled _features _c _str , environment _blend _mode _c _str ) ;
2024-02-15 14:58:38 -06:00
GodotRuntime . free ( reference _space _c _str ) ;
GodotRuntime . free ( enabled _features _c _str ) ;
2024-06-26 21:40:07 -05:00
GodotRuntime . free ( environment _blend _mode _c _str ) ;
2020-09-26 17:15:21 -05:00
} , 0 ) ;
}
function requestReferenceSpace ( ) {
const reference _space _type = requested _reference _space _types . shift ( ) ;
session . requestReferenceSpace ( reference _space _type )
. then ( ( refSpace ) => {
onReferenceSpaceSuccess ( refSpace , reference _space _type ) ;
} )
. catch ( ( ) => {
if ( requested _reference _space _types . length === 0 ) {
const c _str = GodotRuntime . allocString ( 'Unable to get any of the requested reference space types' ) ;
onfailed ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} else {
requestReferenceSpace ( ) ;
}
} ) ;
}
requestReferenceSpace ( ) ;
} ) . catch ( function ( error ) {
const c _str = GodotRuntime . allocString ( ` Unable to make WebGL context compatible with WebXR: ${ error } ` ) ;
onfailed ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} ) ;
} ) . catch ( function ( error ) {
const c _str = GodotRuntime . allocString ( ` Unable to start session: ${ error } ` ) ;
onfailed ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} ) ;
} ,
godot _webxr _uninitialize _ _proxy : 'sync' ,
godot _webxr _uninitialize _ _sig : 'v' ,
godot _webxr _uninitialize : function ( ) {
if ( GodotWebXR . session ) {
GodotWebXR . session . end ( )
// Prevent exception when session has already ended.
. catch ( ( e ) => { } ) ;
}
GodotWebXR . session = null ;
2022-11-18 20:43:11 -06:00
GodotWebXR . gl _binding = null ;
GodotWebXR . layer = null ;
2020-09-26 17:15:21 -05:00
GodotWebXR . space = null ;
GodotWebXR . frame = null ;
GodotWebXR . pose = null ;
2022-11-18 20:43:11 -06:00
GodotWebXR . view _count = 1 ;
GodotWebXR . input _sources = new Array ( 16 ) ;
GodotWebXR . touches = new Array ( 5 ) ;
2023-02-08 21:02:13 -06:00
GodotWebXR . onsimpleevent = null ;
2020-09-26 17:15:21 -05:00
// Disable the monkey-patched window.requestAnimationFrame() and
// pause/restart the main loop to activate it on all platforms.
GodotWebXR . monkeyPatchRequestAnimationFrame ( false ) ;
GodotWebXR . pauseResumeMainLoop ( ) ;
} ,
2021-01-25 08:35:26 -06:00
godot _webxr _get _view _count _ _proxy : 'sync' ,
godot _webxr _get _view _count _ _sig : 'i' ,
godot _webxr _get _view _count : function ( ) {
if ( ! GodotWebXR . session || ! GodotWebXR . pose ) {
2022-11-18 20:43:11 -06:00
return 1 ;
2021-01-25 08:35:26 -06:00
}
2022-11-18 20:43:11 -06:00
const view _count = GodotWebXR . pose . views . length ;
return view _count > 0 ? view _count : 1 ;
2021-01-25 08:35:26 -06:00
} ,
2021-08-23 15:23:16 +10:00
godot _webxr _get _render _target _size _ _proxy : 'sync' ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _render _target _size _ _sig : 'ii' ,
godot _webxr _get _render _target _size : function ( r _size ) {
const subimage = GodotWebXR . getSubImage ( ) ;
if ( subimage === null ) {
return false ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _size + 0 , subimage . viewport . width , 'i32' ) ;
GodotRuntime . setHeapValue ( r _size + 4 , subimage . viewport . height , 'i32' ) ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
return true ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _transform _for _view _ _proxy : 'sync' ,
godot _webxr _get _transform _for _view _ _sig : 'iii' ,
godot _webxr _get _transform _for _view : function ( p _view , r _transform ) {
2020-09-26 17:15:21 -05:00
if ( ! GodotWebXR . session || ! GodotWebXR . pose ) {
2022-11-18 20:43:11 -06:00
return false ;
2020-09-26 17:15:21 -05:00
}
const views = GodotWebXR . pose . views ;
let matrix ;
2022-11-18 20:43:11 -06:00
if ( p _view >= 0 ) {
matrix = views [ p _view ] . transform . matrix ;
2020-09-26 17:15:21 -05:00
} else {
2022-11-18 20:43:11 -06:00
// For -1 (or any other negative value) return the HMD transform.
matrix = GodotWebXR . pose . transform . matrix ;
2020-09-26 17:15:21 -05:00
}
for ( let i = 0 ; i < 16 ; i ++ ) {
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _transform + ( i * 4 ) , matrix [ i ] , 'float' ) ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
return true ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _projection _for _view _ _proxy : 'sync' ,
godot _webxr _get _projection _for _view _ _sig : 'iii' ,
godot _webxr _get _projection _for _view : function ( p _view , r _transform ) {
2020-09-26 17:15:21 -05:00
if ( ! GodotWebXR . session || ! GodotWebXR . pose ) {
2022-11-18 20:43:11 -06:00
return false ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
const matrix = GodotWebXR . pose . views [ p _view ] . projectionMatrix ;
for ( let i = 0 ; i < 16 ; i ++ ) {
GodotRuntime . setHeapValue ( r _transform + ( i * 4 ) , matrix [ i ] , 'float' ) ;
2022-09-04 09:56:24 -05:00
}
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
return true ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _color _texture _ _proxy : 'sync' ,
godot _webxr _get _color _texture _ _sig : 'i' ,
godot _webxr _get _color _texture : function ( ) {
const subimage = GodotWebXR . getSubImage ( ) ;
if ( subimage === null ) {
return 0 ;
}
return GodotWebXR . getTextureId ( subimage . colorTexture ) ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _depth _texture _ _proxy : 'sync' ,
godot _webxr _get _depth _texture _ _sig : 'i' ,
godot _webxr _get _depth _texture : function ( ) {
const subimage = GodotWebXR . getSubImage ( ) ;
if ( subimage === null ) {
2020-09-26 17:15:21 -05:00
return 0 ;
}
2022-11-18 20:43:11 -06:00
if ( ! subimage . depthStencilTexture ) {
return 0 ;
}
return GodotWebXR . getTextureId ( subimage . depthStencilTexture ) ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _velocity _texture _ _proxy : 'sync' ,
godot _webxr _get _velocity _texture _ _sig : 'i' ,
godot _webxr _get _velocity _texture : function ( ) {
const subimage = GodotWebXR . getSubImage ( ) ;
if ( subimage === null ) {
return 0 ;
}
if ( ! subimage . motionVectorTexture ) {
return 0 ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
return GodotWebXR . getTextureId ( subimage . motionVectorTexture ) ;
2020-09-26 17:15:21 -05:00
} ,
2022-11-18 20:43:11 -06:00
godot _webxr _update _input _source _ _proxy : 'sync' ,
2024-02-15 14:58:38 -06:00
godot _webxr _update _input _source _ _sig : 'iiiiiiiiiiiiiii' ,
godot _webxr _update _input _source : function ( p _input _source _id , r _target _pose , r _target _ray _mode , r _touch _index , r _has _grip _pose , r _grip _pose , r _has _standard _mapping , r _button _count , r _buttons , r _axes _count , r _axes , r _has _hand _data , r _hand _joints , r _hand _radii ) {
2020-09-26 17:15:21 -05:00
if ( ! GodotWebXR . session || ! GodotWebXR . frame ) {
return 0 ;
}
2022-11-18 20:43:11 -06:00
if ( p _input _source _id < 0 || p _input _source _id >= GodotWebXR . input _sources . length || ! GodotWebXR . input _sources [ p _input _source _id ] ) {
return false ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
const input _source = GodotWebXR . input _sources [ p _input _source _id ] ;
2020-09-26 17:15:21 -05:00
const frame = GodotWebXR . frame ;
const space = GodotWebXR . space ;
2022-11-18 20:43:11 -06:00
// Target pose.
const target _pose = frame . getPose ( input _source . targetRaySpace , space ) ;
if ( ! target _pose ) {
2020-09-26 17:15:21 -05:00
// This can mean that the controller lost tracking.
2022-11-18 20:43:11 -06:00
return false ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
const target _pose _matrix = target _pose . transform . matrix ;
2020-09-26 17:15:21 -05:00
for ( let i = 0 ; i < 16 ; i ++ ) {
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _target _pose + ( i * 4 ) , target _pose _matrix [ i ] , 'float' ) ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
// Target ray mode.
let target _ray _mode = 0 ;
switch ( input _source . targetRayMode ) {
case 'gaze' :
target _ray _mode = 1 ;
break ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
case 'tracked-pointer' :
target _ray _mode = 2 ;
break ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
case 'screen' :
target _ray _mode = 3 ;
break ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
default :
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _target _ray _mode , target _ray _mode , 'i32' ) ;
// Touch index.
GodotRuntime . setHeapValue ( r _touch _index , GodotWebXR . getTouchIndex ( input _source ) , 'i32' ) ;
// Grip pose.
let has _grip _pose = false ;
if ( input _source . gripSpace ) {
const grip _pose = frame . getPose ( input _source . gripSpace , space ) ;
if ( grip _pose ) {
const grip _pose _matrix = grip _pose . transform . matrix ;
for ( let i = 0 ; i < 16 ; i ++ ) {
GodotRuntime . setHeapValue ( r _grip _pose + ( i * 4 ) , grip _pose _matrix [ i ] , 'float' ) ;
}
has _grip _pose = true ;
}
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _has _grip _pose , has _grip _pose ? 1 : 0 , 'i32' ) ;
// Gamepad data (mapping, buttons and axes).
let has _standard _mapping = false ;
let button _count = 0 ;
let axes _count = 0 ;
if ( input _source . gamepad ) {
if ( input _source . gamepad . mapping === 'xr-standard' ) {
has _standard _mapping = true ;
}
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
button _count = Math . min ( input _source . gamepad . buttons . length , 10 ) ;
for ( let i = 0 ; i < button _count ; i ++ ) {
GodotRuntime . setHeapValue ( r _buttons + ( i * 4 ) , input _source . gamepad . buttons [ i ] . value , 'float' ) ;
}
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
axes _count = Math . min ( input _source . gamepad . axes . length , 10 ) ;
for ( let i = 0 ; i < axes _count ; i ++ ) {
GodotRuntime . setHeapValue ( r _axes + ( i * 4 ) , input _source . gamepad . axes [ i ] , 'float' ) ;
2021-01-08 09:23:05 -06:00
}
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _has _standard _mapping , has _standard _mapping ? 1 : 0 , 'i32' ) ;
GodotRuntime . setHeapValue ( r _button _count , button _count , 'i32' ) ;
GodotRuntime . setHeapValue ( r _axes _count , axes _count , 'i32' ) ;
2024-02-15 14:58:38 -06:00
// Hand tracking data.
let has _hand _data = false ;
2024-02-23 16:22:37 -06:00
if ( input _source . hand && r _hand _joints !== 0 && r _hand _radii !== 0 ) {
2024-02-15 14:58:38 -06:00
const hand _joint _array = new Float32Array ( 25 * 16 ) ;
const hand _radii _array = new Float32Array ( 25 ) ;
if ( frame . fillPoses ( input _source . hand . values ( ) , space , hand _joint _array ) && frame . fillJointRadii ( input _source . hand . values ( ) , hand _radii _array ) ) {
GodotRuntime . heapCopy ( HEAPF32 , hand _joint _array , r _hand _joints ) ;
GodotRuntime . heapCopy ( HEAPF32 , hand _radii _array , r _hand _radii ) ;
has _hand _data = true ;
}
}
GodotRuntime . setHeapValue ( r _has _hand _data , has _hand _data ? 1 : 0 , 'i32' ) ;
2022-11-18 20:43:11 -06:00
return true ;
2020-09-26 17:15:21 -05:00
} ,
godot _webxr _get _visibility _state _ _proxy : 'sync' ,
godot _webxr _get _visibility _state _ _sig : 'i' ,
godot _webxr _get _visibility _state : function ( ) {
if ( ! GodotWebXR . session || ! GodotWebXR . session . visibilityState ) {
return 0 ;
}
return GodotRuntime . allocString ( GodotWebXR . session . visibilityState ) ;
} ,
godot _webxr _get _bounds _geometry _ _proxy : 'sync' ,
2022-11-18 20:43:11 -06:00
godot _webxr _get _bounds _geometry _ _sig : 'ii' ,
godot _webxr _get _bounds _geometry : function ( r _points ) {
2020-09-26 17:15:21 -05:00
if ( ! GodotWebXR . space || ! GodotWebXR . space . boundsGeometry ) {
return 0 ;
}
const point _count = GodotWebXR . space . boundsGeometry . length ;
if ( point _count === 0 ) {
return 0 ;
}
2022-11-18 20:43:11 -06:00
const buf = GodotRuntime . malloc ( point _count * 3 * 4 ) ;
2020-09-26 17:15:21 -05:00
for ( let i = 0 ; i < point _count ; i ++ ) {
const point = GodotWebXR . space . boundsGeometry [ i ] ;
2023-02-10 19:33:18 -06:00
GodotRuntime . setHeapValue ( buf + ( ( i * 3 ) + 0 ) * 4 , point . x , 'float' ) ;
GodotRuntime . setHeapValue ( buf + ( ( i * 3 ) + 1 ) * 4 , point . y , 'float' ) ;
GodotRuntime . setHeapValue ( buf + ( ( i * 3 ) + 2 ) * 4 , point . z , 'float' ) ;
2020-09-26 17:15:21 -05:00
}
2022-11-18 20:43:11 -06:00
GodotRuntime . setHeapValue ( r _points , buf , 'i32' ) ;
2020-09-26 17:15:21 -05:00
2022-11-18 20:43:11 -06:00
return point _count ;
2020-09-26 17:15:21 -05:00
} ,
2023-02-08 21:02:13 -06:00
godot _webxr _get _frame _rate _ _proxy : 'sync' ,
godot _webxr _get _frame _rate _ _sig : 'i' ,
godot _webxr _get _frame _rate : function ( ) {
if ( ! GodotWebXR . session || GodotWebXR . session . frameRate === undefined ) {
return 0 ;
}
return GodotWebXR . session . frameRate ;
} ,
godot _webxr _update _target _frame _rate _ _proxy : 'sync' ,
godot _webxr _update _target _frame _rate _ _sig : 'vi' ,
godot _webxr _update _target _frame _rate : function ( p _frame _rate ) {
if ( ! GodotWebXR . session || GodotWebXR . session . updateTargetFrameRate === undefined ) {
return ;
}
GodotWebXR . session . updateTargetFrameRate ( p _frame _rate ) . then ( ( ) => {
const c _str = GodotRuntime . allocString ( 'display_refresh_rate_changed' ) ;
GodotWebXR . onsimpleevent ( c _str ) ;
GodotRuntime . free ( c _str ) ;
} ) ;
} ,
godot _webxr _get _supported _frame _rates _ _proxy : 'sync' ,
godot _webxr _get _supported _frame _rates _ _sig : 'ii' ,
godot _webxr _get _supported _frame _rates : function ( r _frame _rates ) {
if ( ! GodotWebXR . session || GodotWebXR . session . supportedFrameRates === undefined ) {
return 0 ;
}
const frame _rate _count = GodotWebXR . session . supportedFrameRates . length ;
if ( frame _rate _count === 0 ) {
return 0 ;
}
const buf = GodotRuntime . malloc ( frame _rate _count * 4 ) ;
for ( let i = 0 ; i < frame _rate _count ; i ++ ) {
GodotRuntime . setHeapValue ( buf + ( i * 4 ) , GodotWebXR . session . supportedFrameRates [ i ] , 'float' ) ;
}
GodotRuntime . setHeapValue ( r _frame _rates , buf , 'i32' ) ;
return frame _rate _count ;
} ,
2020-09-26 17:15:21 -05:00
} ;
autoAddDeps ( GodotWebXR , '$GodotWebXR' ) ;
mergeInto ( LibraryManager . library , GodotWebXR ) ;