allow empty xrf
This commit is contained in:
parent
13f96e0506
commit
49cdf18472
7 changed files with 4797 additions and 345 deletions
214
dist/xrfragment.aframe.all.js
vendored
214
dist/xrfragment.aframe.all.js
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023
|
||||
* v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
|
||||
* https://xrfragment.org
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
|
@ -2341,6 +2341,8 @@ xrf.addEventListener('t', (opts) => {
|
|||
})
|
||||
window.AFRAME.registerComponent('xrf', {
|
||||
schema: {
|
||||
http: { type:'string'},
|
||||
https: { type:'string'},
|
||||
},
|
||||
init: function () {
|
||||
if( !AFRAME.XRF ){
|
||||
|
|
@ -2350,112 +2352,116 @@ window.AFRAME.registerComponent('xrf', {
|
|||
camera.setAttribute('xrf-fade','')
|
||||
AFRAME.fade = camera.components['xrf-fade']
|
||||
|
||||
let aScene = document.querySelector('a-scene')
|
||||
aScene.addEventListener('loaded', () => {
|
||||
let aScene = AFRAME.scenes[0]
|
||||
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
AFRAME.XRF.navigator.to(this.data)
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
if( typeof this.data === 'string' || this.data.http || this.data.https ){
|
||||
let url
|
||||
if( typeof this.data === 'string' ) url = this.data
|
||||
if( this.data.http ) url = `http:${this.data.http}`
|
||||
if( this.data.https ) url = `https:${this.data.https}`
|
||||
AFRAME.XRF.navigator.to( url )
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
}
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
}
|
||||
|
||||
if( typeof this.data == "string" ){
|
||||
|
|
|
|||
214
dist/xrfragment.aframe.js
vendored
214
dist/xrfragment.aframe.js
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* v0.5.1 generated at Sat Dec 30 09:46:41 PM UTC 2023
|
||||
* v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
|
||||
* https://xrfragment.org
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
|
@ -2339,6 +2339,8 @@ xrf.addEventListener('t', (opts) => {
|
|||
})
|
||||
window.AFRAME.registerComponent('xrf', {
|
||||
schema: {
|
||||
http: { type:'string'},
|
||||
https: { type:'string'},
|
||||
},
|
||||
init: function () {
|
||||
if( !AFRAME.XRF ){
|
||||
|
|
@ -2348,112 +2350,116 @@ window.AFRAME.registerComponent('xrf', {
|
|||
camera.setAttribute('xrf-fade','')
|
||||
AFRAME.fade = camera.components['xrf-fade']
|
||||
|
||||
let aScene = document.querySelector('a-scene')
|
||||
aScene.addEventListener('loaded', () => {
|
||||
let aScene = AFRAME.scenes[0]
|
||||
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
AFRAME.XRF.navigator.to(this.data)
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
if( typeof this.data === 'string' || this.data.http || this.data.https ){
|
||||
let url
|
||||
if( typeof this.data === 'string' ) url = this.data
|
||||
if( this.data.http ) url = `http:${this.data.http}`
|
||||
if( this.data.https ) url = `https:${this.data.https}`
|
||||
AFRAME.XRF.navigator.to( url )
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
}
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
}
|
||||
|
||||
if( typeof this.data == "string" ){
|
||||
|
|
|
|||
4432
dist/xrfragment.module.js
vendored
4432
dist/xrfragment.module.js
vendored
File diff suppressed because it is too large
Load diff
2
dist/xrfragment.three.js
vendored
2
dist/xrfragment.three.js
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023
|
||||
* v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
|
||||
* https://xrfragment.org
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
|
|
|||
2
dist/xrfragment.three.module.js
vendored
2
dist/xrfragment.three.module.js
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* v0.5.1 generated at Sat Dec 30 09:46:42 PM UTC 2023
|
||||
* v0.5.1 generated at Wed Jan 3 03:55:09 PM UTC 2024
|
||||
* https://xrfragment.org
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -30,42 +30,44 @@
|
|||
<!-- -->
|
||||
<!-- everything below is completely optional, but handy to add a quick menu / connectivity to your space -->
|
||||
<!-- -->
|
||||
<script>
|
||||
|
||||
document.addEventListener('$menu:ready', (e) => {
|
||||
let {$menu} = e.detail
|
||||
// add your menubuttons:
|
||||
$menu.buttons = $menu.buttons.concat([
|
||||
`<a class="btn" aria-label="button" aria-title="menu button" onclick="$menu.showAbout()"><i class="gg-info"></i> about</a><br>`
|
||||
])
|
||||
$menu.collapsed = false
|
||||
$menu.showAbout = () => window.notify(`
|
||||
<h1>💁 Hi there!</h1>
|
||||
|
||||
This XR fragments experience works almost anywhere.<br>
|
||||
Allowing rich audiovisual events with(out)<br>
|
||||
VR/AR headsets (it's awesome though ♥️)<br>
|
||||
<br>
|
||||
<b>This uses only open standards:</b><br>
|
||||
📦 surf 3D models using URLs<br>
|
||||
🧑🤝🧑 <b>instant</b> meetings inside hyperlinked 3D models<br>
|
||||
🚫 <b>zero proprietary</b> tech/game engines or platforms<br>
|
||||
🙈 unhosted, privacy-friendly, avatar-agnostic scenes<br>
|
||||
🔗 <a href="https://xrfragment.org" target="_blank">XR Fragments</a> for 3D <b>hyper-linking</b> & navigation<br>
|
||||
📷 Serverless & encrypted <a href="https://webrtc.org" target="_blank">P2P WebRTC</a> using <a href="https://github.com/dmotz/trystero" target="_blank">trystero</a><br>
|
||||
🦍 <a href="https://immersiveweb.dev" target="_blank">WebXR</a> using <a href="https://aframe.io" target="_blank">AFRAME</a> + <a href="https://three.org" target="_blank">Three.js</a><br>
|
||||
🙉 go selfhost <a href="https://github.com/coderofsalvation/xrfragment-helloworld">worlds-in-a-webpage</a><br>
|
||||
♥️ Be sustainable: go 100% <a href="https://www.forbes.com/sites/adrianbridgwater/2023/02/06/the-future-for-open-source/" target="_blank">opensource</a>
|
||||
<br><br>
|
||||
<a href="https://nlnet.nl" target="_blank">
|
||||
<img src="https://nlnet.nl/image/logo_nlnet.svg" width="100"/>
|
||||
</a>
|
||||
<br><br>
|
||||
`,{timeout:false})
|
||||
})
|
||||
</script>
|
||||
<script src="./../../../dist/aframe-blink-controls.min.js"></script> <!-- teleporting using controllers -->
|
||||
<script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- menu + chat interface -->
|
||||
<script src="./../../../dist/xrfragment.plugin.p2p.js"></script> <!-- serverless p2p connectivity -->
|
||||
<script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity -->
|
||||
<script>
|
||||
$menu.logo = 'logo_file_or_url_here'
|
||||
$menu.morelabel = '⚡'
|
||||
// add your menubuttons:
|
||||
$menu.buttons = $menu.buttons.concat([`<a class="btn" aria-label="button" aria-title="menu button" aria-description="about menu" onclick="$menu.showAbout()">💁 about</a><br>`])
|
||||
$menu.collapsed = false
|
||||
$menu.showAbout = () => window.notify(`
|
||||
<h1>💁 Hi there!</h1>
|
||||
|
||||
This XR fragments experience works almost anywhere.<br>
|
||||
Allowing rich audiovisual events with(out)<br>
|
||||
VR/AR headsets (it's awesome though ♥️)<br>
|
||||
<br>
|
||||
<b>This uses only open standards:</b><br>
|
||||
📦 surf 3D models using URLs<br>
|
||||
🧑🤝🧑 <b>instant</b> meetings inside hyperlinked 3D models<br>
|
||||
🚫 <b>zero proprietary</b> tech/game engines or platforms<br>
|
||||
🙈 unhosted, privacy-friendly, avatar-agnostic scenes<br>
|
||||
🔗 <a href="https://xrfragment.org" target="_blank">XR Fragments</a> for 3D <b>hyper-linking</b> & navigation<br>
|
||||
📷 Serverless & encrypted <a href="https://webrtc.org" target="_blank">P2P WebRTC</a> using <a href="https://github.com/dmotz/trystero" target="_blank">trystero</a><br>
|
||||
🦍 <a href="https://immersiveweb.dev" target="_blank">WebXR</a> using <a href="https://aframe.io" target="_blank">AFRAME</a> + <a href="https://three.org" target="_blank">Three.js</a><br>
|
||||
🙉 go selfhost <a href="https://github.com/coderofsalvation/xrfragment-helloworld">worlds-in-a-webpage</a><br>
|
||||
♥️ Be sustainable: go 100% <a href="https://www.forbes.com/sites/adrianbridgwater/2023/02/06/the-future-for-open-source/" target="_blank">opensource</a>
|
||||
<br><br>
|
||||
<a href="https://nlnet.nl" target="_blank">
|
||||
<img src="https://nlnet.nl/image/logo_nlnet.svg" width="100"/>
|
||||
</a>
|
||||
<br><br>
|
||||
`,{timeout:false})
|
||||
|
||||
$menu.install(xrf)
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
window.AFRAME.registerComponent('xrf', {
|
||||
schema: {
|
||||
http: { type:'string'},
|
||||
https: { type:'string'},
|
||||
},
|
||||
init: function () {
|
||||
if( !AFRAME.XRF ){
|
||||
|
|
@ -9,112 +11,116 @@ window.AFRAME.registerComponent('xrf', {
|
|||
camera.setAttribute('xrf-fade','')
|
||||
AFRAME.fade = camera.components['xrf-fade']
|
||||
|
||||
let aScene = document.querySelector('a-scene')
|
||||
aScene.addEventListener('loaded', () => {
|
||||
let aScene = AFRAME.scenes[0]
|
||||
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
// enable XR fragments
|
||||
let XRF = AFRAME.XRF = xrf.init({
|
||||
THREE,
|
||||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
AFRAME.XRF.navigator.to(this.data)
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
// this is just for convenience (not part of spec): hide/show stuff based on VR/AR tags in 3D model
|
||||
ARbutton = document.querySelector('.a-enter-ar-button')
|
||||
VRbutton = document.querySelector('.a-enter-vr-button')
|
||||
if( ARbutton ) ARbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#AR' ) )
|
||||
if( VRbutton ) VRbutton.addEventListener('click', () => AFRAME.XRF.hashbus.pub( '#VR' ) )
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => {
|
||||
setTimeout( () => AFRAME.fade.out(),500)
|
||||
|
||||
// *TODO* this does not really belong here perhaps
|
||||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
$('a-scene').appendChild(el)
|
||||
})
|
||||
blinkControls.components['blink-controls'].update({collisionEntities:true})
|
||||
}
|
||||
})
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
let p = opts.promise()
|
||||
let url = opts.xrf.string
|
||||
let isLocal = url.match(/^#/)
|
||||
let hasPos = url.match(/pos=/)
|
||||
if( !isLocal && !url.match(/^http/) ) return // dont fade/load for custom protocol handlers
|
||||
if( isLocal && hasPos ){
|
||||
// local teleports only
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else if( !isLocal ){
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}else p.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
// convert href's to a-entity's so AFRAME
|
||||
// raycaster can find & execute it
|
||||
AFRAME.XRF.clickableMeshToEntity = (opts) => {
|
||||
let {mesh,clickHandler} = opts;
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
||||
// cleanup xrf-get objects when resetting scene
|
||||
xrf.addEventListener('reset', (opts) => {
|
||||
let els = [...document.querySelectorAll('[xrf-get]')]
|
||||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
if( typeof this.data === 'string' || this.data.http || this.data.https ){
|
||||
let url
|
||||
if( typeof this.data === 'string' ) url = this.data
|
||||
if( this.data.http ) url = `http:${this.data.http}`
|
||||
if( this.data.https ) url = `https:${this.data.https}`
|
||||
AFRAME.XRF.navigator.to( url )
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
gets.map( (g) => g.emit('update') )
|
||||
})
|
||||
}
|
||||
|
||||
aScene.emit('XRF',{})
|
||||
|
||||
// enable gaze-click on Mobile VR
|
||||
aScene.setAttribute('xrf-gaze','')
|
||||
|
||||
}
|
||||
|
||||
if( typeof this.data == "string" ){
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue