better gyroscope + touchsupport

This commit is contained in:
Leon van Kammen 2024-06-15 17:33:08 +02:00
parent 720b17f75c
commit f508d1e262
13 changed files with 45909 additions and 146 deletions

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Wed Jun 12 08:50:44 AM UTC 2024 * v0.5.1 generated at Sat Jun 15 05:22:38 PM CEST 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1942,6 +1942,39 @@ xrf.parseModel = function(model,url){
xrf.emit('parseModel',{model,url,file}) xrf.emit('parseModel',{model,url,file})
} }
xrf.loadModel = function(model,url,noadd){
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fragment,fileExt} = URI;
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
}
xrf.parseModel.metadataInMesh = (mesh,model) => { xrf.parseModel.metadataInMesh = (mesh,model) => {
if( mesh.userData ){ if( mesh.userData ){
let frag = {} let frag = {}
@ -2065,34 +2098,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader = loader || new Loader().setPath( URI.URN ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
xrf.loadModel(model,url)
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -2996,6 +3002,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
element.addEventListener( 'mousemove', onPointerEvent ); element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent ); element.addEventListener( 'click', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent ); element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'touchstart', onPointerEvent );
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
@ -3003,6 +3010,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const eventsMapper = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'touchstart': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
'selectend': 'mouseup' 'selectend': 'mouseup'
}; };
@ -3951,7 +3959,7 @@ xrf.portalNonEuclidian = function(opts){
if( mesh.userData.XRF.href ){ if( mesh.userData.XRF.href ){
raycaster.far = 0.35 raycaster.far = 0.35
raycaster.set(cameraPosition, cameraDirection ) raycaster.set(cameraPosition, cameraDirection )
intersects = raycaster.intersectObjects([mesh], false) let intersects = raycaster.intersectObjects([mesh], false)
if (intersects.length > 0 && !mesh.portal.teleporting ){ if (intersects.length > 0 && !mesh.portal.teleporting ){
mesh.portal.teleporting = true mesh.portal.teleporting = true
mesh.userData.XRF.href.exec({nocommit:true}) mesh.userData.XRF.href.exec({nocommit:true})
@ -4847,7 +4855,7 @@ AFRAME.registerComponent('xrf-gaze',{
} }
}, },
setGazer: function(state, fuse){ setGazer: function(state, fuse){
if( !AFRAME.utils.device.isMobile() ) return if( this.el.sceneEl.getAttribute("xrf-gaze-always") == undefined && !AFRAME.utils.device.isMobile() ) return
let cam = document.querySelector("[camera]") let cam = document.querySelector("[camera]")
if( state ){ if( state ){
if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Wed Jun 12 08:50:44 AM UTC 2024 * v0.5.1 generated at Sat Jun 15 05:22:37 PM CEST 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1940,6 +1940,39 @@ xrf.parseModel = function(model,url){
xrf.emit('parseModel',{model,url,file}) xrf.emit('parseModel',{model,url,file})
} }
xrf.loadModel = function(model,url,noadd){
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fragment,fileExt} = URI;
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
}
xrf.parseModel.metadataInMesh = (mesh,model) => { xrf.parseModel.metadataInMesh = (mesh,model) => {
if( mesh.userData ){ if( mesh.userData ){
let frag = {} let frag = {}
@ -2063,34 +2096,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader = loader || new Loader().setPath( URI.URN ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
xrf.loadModel(model,url)
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -2994,6 +3000,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
element.addEventListener( 'mousemove', onPointerEvent ); element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent ); element.addEventListener( 'click', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent ); element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'touchstart', onPointerEvent );
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
@ -3001,6 +3008,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const eventsMapper = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'touchstart': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
'selectend': 'mouseup' 'selectend': 'mouseup'
}; };
@ -3949,7 +3957,7 @@ xrf.portalNonEuclidian = function(opts){
if( mesh.userData.XRF.href ){ if( mesh.userData.XRF.href ){
raycaster.far = 0.35 raycaster.far = 0.35
raycaster.set(cameraPosition, cameraDirection ) raycaster.set(cameraPosition, cameraDirection )
intersects = raycaster.intersectObjects([mesh], false) let intersects = raycaster.intersectObjects([mesh], false)
if (intersects.length > 0 && !mesh.portal.teleporting ){ if (intersects.length > 0 && !mesh.portal.teleporting ){
mesh.portal.teleporting = true mesh.portal.teleporting = true
mesh.userData.XRF.href.exec({nocommit:true}) mesh.userData.XRF.href.exec({nocommit:true})
@ -4845,7 +4853,7 @@ AFRAME.registerComponent('xrf-gaze',{
} }
}, },
setGazer: function(state, fuse){ setGazer: function(state, fuse){
if( !AFRAME.utils.device.isMobile() ) return if( this.el.sceneEl.getAttribute("xrf-gaze-always") == undefined && !AFRAME.utils.device.isMobile() ) return
let cam = document.querySelector("[camera]") let cam = document.querySelector("[camera]")
if( state ){ if( state ){
if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls

45733
dist/xrfragment.module.js vendored

File diff suppressed because it is too large Load diff

View file

@ -357,7 +357,7 @@ window.accessibility = (opts) => new Proxy({
setTimeout( () => this.initCommands(), 200 ) setTimeout( () => this.initCommands(), 200 )
// auto-enable if previously enabled // auto-enable if previously enabled
if( window.localStorage.getItem("accessibility") === 'true' ){ if( window.localStorage.getItem("accessibility") === 'true' || xrf.navigator.URI.XRF.accessible ){
setTimeout( () => { setTimeout( () => {
this.enabled = true this.enabled = true
this.setFontSize() this.setFontSize()
@ -437,7 +437,6 @@ window.accessibility = (opts) => new Proxy({
switch( k ){ switch( k ){
case "enabled": { case "enabled": {
let message = "accessibility mode has been "+(v?"activated":"disabled")+".<br>Type /help for help." let message = "accessibility mode has been "+(v?"activated":"disabled")+".<br>Type /help for help."
if( v ) message = "<img src='https://i.imgur.com/wedtUSs.png' style='width:100%;border-radius:6px'/><br>" + message
$('#accessibility.btn').style.filter= v ? 'brightness(1.0)' : 'brightness(0.5)' $('#accessibility.btn').style.filter= v ? 'brightness(1.0)' : 'brightness(0.5)'
if( v ) $chat.visible = true if( v ) $chat.visible = true
$chat.send({message,class:['info']}) $chat.send({message,class:['info']})
@ -467,8 +466,8 @@ document.addEventListener('$menu:ready', (e) => {
document.querySelector('head').innerHTML += ` document.querySelector('head').innerHTML += `
<style type="text/css"> <style type="text/css">
.accessibility #messages * { .accessibility #messages * {
font-size:24px !important; font-size:20px !important;
line-height:40px; line-height:35px;
} }
.accessibility #messages .msg.self { .accessibility #messages .msg.self {
background:var(--xrf-gray); background:var(--xrf-gray);
@ -682,8 +681,7 @@ window.frontend = (opts) => new Proxy({
let root = data.mesh.portal ? data.mesh.portal.stencilObject : data.mesh let root = data.mesh.portal ? data.mesh.portal.stencilObject : data.mesh
let transcript = xrf.sceneToTranscript(root,data.mesh) let transcript = xrf.sceneToTranscript(root,data.mesh)
if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>` if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>`
if (hasMeta && !data.mesh.portal && metadata.XRF.src ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
if (hasMeta && !data.mesh.portal ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) }) window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) })
}) })
@ -694,7 +692,6 @@ window.frontend = (opts) => new Proxy({
setupNetworkListeners(){ setupNetworkListeners(){
document.addEventListener('network.connect', (e) => { document.addEventListener('network.connect', (e) => {
console.log("network.connect")
window.notify("🪐 connecting to awesomeness..") window.notify("🪐 connecting to awesomeness..")
$chat.send({message:`🪐 connecting to awesomeness..`,class:['info'], timeout:5000}) $chat.send({message:`🪐 connecting to awesomeness..`,class:['info'], timeout:5000})
}) })

View file

@ -660,8 +660,8 @@ chatComponent.css = `
/* /*
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 91%; width: 100%;
max-width: 500px; max-width: 40%;
*/ */
width:100%; width:100%;
box-sizing:border-box; box-sizing:border-box;
@ -816,8 +816,8 @@ chatComponent.css = `
.envelope{ .envelope{
margin-right:15px; margin-right:15px;
width:50%; width:100%;
max-width:700px; max-width:40%;
} }
.envelope, .envelope,

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Wed Jun 12 08:50:44 AM UTC 2024 * v0.5.1 generated at Sat Jun 15 05:22:38 PM CEST 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1940,6 +1940,39 @@ xrf.parseModel = function(model,url){
xrf.emit('parseModel',{model,url,file}) xrf.emit('parseModel',{model,url,file})
} }
xrf.loadModel = function(model,url,noadd){
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fragment,fileExt} = URI;
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
}
xrf.parseModel.metadataInMesh = (mesh,model) => { xrf.parseModel.metadataInMesh = (mesh,model) => {
if( mesh.userData ){ if( mesh.userData ){
let frag = {} let frag = {}
@ -2063,34 +2096,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader = loader || new Loader().setPath( URI.URN ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
xrf.loadModel(model,url)
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -2994,6 +3000,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
element.addEventListener( 'mousemove', onPointerEvent ); element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent ); element.addEventListener( 'click', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent ); element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'touchstart', onPointerEvent );
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
@ -3001,6 +3008,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const eventsMapper = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'touchstart': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
'selectend': 'mouseup' 'selectend': 'mouseup'
}; };
@ -3949,7 +3957,7 @@ xrf.portalNonEuclidian = function(opts){
if( mesh.userData.XRF.href ){ if( mesh.userData.XRF.href ){
raycaster.far = 0.35 raycaster.far = 0.35
raycaster.set(cameraPosition, cameraDirection ) raycaster.set(cameraPosition, cameraDirection )
intersects = raycaster.intersectObjects([mesh], false) let intersects = raycaster.intersectObjects([mesh], false)
if (intersects.length > 0 && !mesh.portal.teleporting ){ if (intersects.length > 0 && !mesh.portal.teleporting ){
mesh.portal.teleporting = true mesh.portal.teleporting = true
mesh.userData.XRF.href.exec({nocommit:true}) mesh.userData.XRF.href.exec({nocommit:true})

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Wed Jun 12 08:50:44 AM UTC 2024 * v0.5.1 generated at Sat Jun 15 05:22:38 PM CEST 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1940,6 +1940,39 @@ xrf.parseModel = function(model,url){
xrf.emit('parseModel',{model,url,file}) xrf.emit('parseModel',{model,url,file})
} }
xrf.loadModel = function(model,url,noadd){
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fragment,fileExt} = URI;
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
}
xrf.parseModel.metadataInMesh = (mesh,model) => { xrf.parseModel.metadataInMesh = (mesh,model) => {
if( mesh.userData ){ if( mesh.userData ){
let frag = {} let frag = {}
@ -2063,34 +2096,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader = loader || new Loader().setPath( URI.URN ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
xrf.loadModel(model,url)
model.file = URI.file
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
// spec: 1. generate the XRWG
xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene )
// only change url when loading *another* file
fragment = fragment || defaultFragment || ''
xrf.navigator.pushState( URI.external ? URI.URN + URI.file : URI.file, fragment.replace(/^#/,'') )
//if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -2994,6 +3000,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
element.addEventListener( 'mousemove', onPointerEvent ); element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent ); element.addEventListener( 'click', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent ); element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'touchstart', onPointerEvent );
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
@ -3001,6 +3008,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const eventsMapper = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'touchstart': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
'selectend': 'mouseup' 'selectend': 'mouseup'
}; };
@ -3949,7 +3957,7 @@ xrf.portalNonEuclidian = function(opts){
if( mesh.userData.XRF.href ){ if( mesh.userData.XRF.href ){
raycaster.far = 0.35 raycaster.far = 0.35
raycaster.set(cameraPosition, cameraDirection ) raycaster.set(cameraPosition, cameraDirection )
intersects = raycaster.intersectObjects([mesh], false) let intersects = raycaster.intersectObjects([mesh], false)
if (intersects.length > 0 && !mesh.portal.teleporting ){ if (intersects.length > 0 && !mesh.portal.teleporting ){
mesh.portal.teleporting = true mesh.portal.teleporting = true
mesh.userData.XRF.href.exec({nocommit:true}) mesh.userData.XRF.href.exec({nocommit:true})

View file

@ -16,8 +16,9 @@
<body> <body>
<a-scene xr-mode-ui="XRMode: xr" <a-scene xr-mode-ui="XRMode: xr"
renderer="colorManagement: false; antialias:true; highRefreshRate:true; foveationLevel: 0.5; toneMapping: ACESFilmic; exposure: 3.0" renderer="colorManagement: false; antialias:true; highRefreshRate:true; foveationLevel: 0.5; toneMapping: ACESFilmic; exposure: 3.0"
device-orientation-permission-ui
light="defaultLightsEnabled: false"> light="defaultLightsEnabled: false">
<a-entity id="player" movement-controls touch-controls wasd-controls="fly:false" look-controls> <a-entity id="player" movement-controls touch-controls wasd-controls="fly:false" look-controls="magicWindowTrackingEnabled:true">
<a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity> <a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity>
<a-entity id="left-hand" hand-tracking-grab-controls="hand:left;modelColor:#cccccc" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor"> <a-entity id="left-hand" hand-tracking-grab-controls="hand:left;modelColor:#cccccc" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: .floor">
<a-entity rotation="-35 0 0" position="0 0.1 0" id="navigator"> <a-entity rotation="-35 0 0" position="0 0.1 0" id="navigator">
@ -69,6 +70,7 @@
<br><br> <br><br>
`,{timeout:false}) `,{timeout:false})
}) })
</script> </script>

View file

@ -12,7 +12,7 @@ AFRAME.registerComponent('xrf-gaze',{
} }
}, },
setGazer: function(state, fuse){ setGazer: function(state, fuse){
if( !AFRAME.utils.device.isMobile() ) return if( this.el.sceneEl.getAttribute("xrf-gaze-always") == undefined && !AFRAME.utils.device.isMobile() ) return
let cam = document.querySelector("[camera]") let cam = document.querySelector("[camera]")
if( state ){ if( state ){
if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls if( cam.innerHTML.match(/cursor/) ) return; // avoid duplicate calls

View file

@ -275,8 +275,8 @@ chatComponent.css = `
/* /*
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 91%; width: 100%;
max-width: 500px; max-width: 40%;
*/ */
width:100%; width:100%;
box-sizing:border-box; box-sizing:border-box;
@ -431,8 +431,8 @@ chatComponent.css = `
.envelope{ .envelope{
margin-right:15px; margin-right:15px;
width:50%; width:100%;
max-width:700px; max-width:40%;
} }
.envelope, .envelope,

View file

@ -105,7 +105,7 @@ window.accessibility = (opts) => new Proxy({
setTimeout( () => this.initCommands(), 200 ) setTimeout( () => this.initCommands(), 200 )
// auto-enable if previously enabled // auto-enable if previously enabled
if( window.localStorage.getItem("accessibility") === 'true' ){ if( window.localStorage.getItem("accessibility") === 'true' || xrf.navigator.URI.XRF.accessible ){
setTimeout( () => { setTimeout( () => {
this.enabled = true this.enabled = true
this.setFontSize() this.setFontSize()
@ -185,7 +185,6 @@ window.accessibility = (opts) => new Proxy({
switch( k ){ switch( k ){
case "enabled": { case "enabled": {
let message = "accessibility mode has been "+(v?"activated":"disabled")+".<br>Type /help for help." let message = "accessibility mode has been "+(v?"activated":"disabled")+".<br>Type /help for help."
if( v ) message = "<img src='https://i.imgur.com/wedtUSs.png' style='width:100%;border-radius:6px'/><br>" + message
$('#accessibility.btn').style.filter= v ? 'brightness(1.0)' : 'brightness(0.5)' $('#accessibility.btn').style.filter= v ? 'brightness(1.0)' : 'brightness(0.5)'
if( v ) $chat.visible = true if( v ) $chat.visible = true
$chat.send({message,class:['info']}) $chat.send({message,class:['info']})
@ -215,8 +214,8 @@ document.addEventListener('$menu:ready', (e) => {
document.querySelector('head').innerHTML += ` document.querySelector('head').innerHTML += `
<style type="text/css"> <style type="text/css">
.accessibility #messages * { .accessibility #messages * {
font-size:24px !important; font-size:20px !important;
line-height:40px; line-height:35px;
} }
.accessibility #messages .msg.self { .accessibility #messages .msg.self {
background:var(--xrf-gray); background:var(--xrf-gray);

View file

@ -122,8 +122,7 @@ window.frontend = (opts) => new Proxy({
let root = data.mesh.portal ? data.mesh.portal.stencilObject : data.mesh let root = data.mesh.portal ? data.mesh.portal.stencilObject : data.mesh
let transcript = xrf.sceneToTranscript(root,data.mesh) let transcript = xrf.sceneToTranscript(root,data.mesh)
if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>` if( transcript.length ) html += `<br><b>transcript:</b><br><div class="transcript">${transcript}</div>`
if (hasMeta && !data.mesh.portal && metadata.XRF.src ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
if (hasMeta && !data.mesh.portal ) html += `<br><br><a class="btn" style="float:right" onclick="xrf.navigator.to('${data.mesh.userData.href}')">Visit embedded scene</a>`
window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) }) window.notify(html,{timeout: 7000 * (hasMeta ? 1.5 : 1) })
}) })
@ -134,7 +133,6 @@ window.frontend = (opts) => new Proxy({
setupNetworkListeners(){ setupNetworkListeners(){
document.addEventListener('network.connect', (e) => { document.addEventListener('network.connect', (e) => {
console.log("network.connect")
window.notify("🪐 connecting to awesomeness..") window.notify("🪐 connecting to awesomeness..")
$chat.send({message:`🪐 connecting to awesomeness..`,class:['info'], timeout:5000}) $chat.send({message:`🪐 connecting to awesomeness..`,class:['info'], timeout:5000})
}) })

View file

@ -85,6 +85,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
element.addEventListener( 'mousemove', onPointerEvent ); element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent ); element.addEventListener( 'click', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent ); element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'touchstart', onPointerEvent );
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
@ -92,6 +93,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const eventsMapper = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'touchstart': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
'selectend': 'mouseup' 'selectend': 'mouseup'
}; };