both aframe and threejs events now bubble up

This commit is contained in:
Leon van Kammen 2024-02-12 17:21:40 +00:00
parent b321096eb9
commit a47f8b60ed
9 changed files with 82 additions and 60 deletions

View file

@ -84,6 +84,9 @@ window.AFRAME.registerComponent('xrf', {
} }
}) })
xrf.addEventListener('navigateError', (opts) => {
AFRAME.fade.out()
})
xrf.addEventListener('navigateLoading', (opts) => { xrf.addEventListener('navigateLoading', (opts) => {
let p = opts.promise() let p = opts.promise()
@ -112,8 +115,9 @@ window.AFRAME.registerComponent('xrf', {
// raycaster can find & execute it // raycaster can find & execute it
AFRAME.XRF.clickableMeshToEntity = (opts) => { AFRAME.XRF.clickableMeshToEntity = (opts) => {
let {mesh,clickHandler} = opts; let {mesh,clickHandler} = opts;
let createEl = function(c){
let el = document.createElement("a-entity") let el = document.createElement("a-entity")
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity el.setAttribute("xrf-get",c.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click el.setAttribute("pressable", '') // detect hand-controller click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html) // respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
@ -123,6 +127,8 @@ window.AFRAME.registerComponent('xrf', {
el.addEventListener("pressedstarted", clickHandler ) el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el) $('a-scene').appendChild(el)
} }
createEl(mesh)
}
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity ) xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
// cleanup xrf-get objects when resetting scene // cleanup xrf-get objects when resetting scene

View file

@ -78,7 +78,14 @@ xrf.navigator.to = (url,flags,loader,data) => {
if( data ){ // file upload if( data ){ // file upload
loader.parse(data, "", onLoad ) loader.parse(data, "", onLoad )
}else loader.load(url, onLoad ) }else{
try{
loader.load(url, onLoad )
}catch(e){
console.error(e)
xrf.emit('navigateError',{url})
}
}
}) })
}) })
}) })
@ -116,6 +123,7 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.addEventListener('navigate', (opts) => { xrf.addEventListener('navigate', (opts) => {
let {url} = opts let {url} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
// handle http links // handle http links
if( url.match(/^http/) && !xrf.loaders[ext] ){ if( url.match(/^http/) && !xrf.loaders[ext] ){
let inIframe let inIframe

View file

@ -30,14 +30,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
const raycaster = new Raycaster(); const raycaster = new Raycaster();
const tempMatrix = new Matrix4(); const tempMatrix = new Matrix4();
let dispatchEvent = (object,_event) => {
object.dispatchEvent(_event)
// bubble up
object.traverseAncestors( (n) => n.userData && n.userData.href && n.dispatchEvent(_event) )
}
// Pointer Events // Pointer Events
const element = renderer.domElement; const element = renderer.domElement;
function onPointerEvent( event ) { function onPointerEvent( event ) {
@ -62,12 +55,12 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
_event.type = event.type; _event.type = event.type;
_event.data.set( uv.x, 1 - uv.y ); _event.data.set( uv.x, 1 - uv.y );
dispatchEvent( object, _event ); object.dispatchEvent( _event );
}else{ }else{
if( object.selected ) { if( object.selected ) {
_event.type = 'mouseleave' _event.type = 'mouseleave'
dispatchEvent( object, _event) object.dispatchEvent( _event)
} }
} }
@ -84,7 +77,7 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
// WebXR Controller Events // WebXR Controller Events
// TODO: Dispatch pointerevents too // TODO: Dispatch pointerevents too
const events = { const eventsMapper = {
'move': 'mousemove', 'move': 'mousemove',
'select': 'click', 'select': 'click',
'selectstart': 'mousedown', 'selectstart': 'mousedown',
@ -104,20 +97,22 @@ xrf.interactiveGroup = function(THREE,renderer,camera){
if ( intersections.length > 0 ) { if ( intersections.length > 0 ) {
console.log(object.name)
const intersection = intersections[ 0 ]; const intersection = intersections[ 0 ];
object = intersection.object; object = intersection.object;
const uv = intersection.uv; const uv = intersection.uv;
_event.type = events[ event.type ]; _event.type = eventsMapper[ event.type ];
_event.data.set( uv.x, 1 - uv.y ); _event.data.set( uv.x, 1 - uv.y );
dispatchEvent( object, _event ); object.dispatchEvent( _event );
}else{ }else{
if( object.selected ) { if( object.selected ) {
_event.type = 'mouseleave' _event.type = 'mouseleave'
dispatchEvent( object, _event) object.dispatchEvent(_event)
} }
} }

View file

@ -38,19 +38,16 @@ xrf.addEventListener('dynamicKeyValue', (opts) => {
if( match.length > 0 ){ if( match.length > 0 ){
xrf.frag.dynamic.material(v,opts) xrf.frag.dynamic.material(v,opts)
}else{ }else{
if( !xrf.URI.vars[ v.string ] ) return // only assign to known values if( !xrf.URI.vars[ v.string ] ) return console.warn(`'${v.string}' metadata not found in scene`) // only assign to known values
xrf.URI.vars[ id ] = xrf.URI.vars[ v.string ] // update var xrf.URI.vars[ id ] = xrf.URI.vars[ v.string ] // update var
if( xrf.debug ) console.log(`URI.vars[${id}]='${v.string}'`) if( xrf.debug ) console.log(`URI.vars[${id}]='${v.string}'`)
xrf.scene.traverse( (n) => { // reflect new changes xrf.scene.traverse( (n) => { // reflect new changes
if( n.userData && n.userData.src && n.userData.srcTemplate ){ if( n.userData && n.userData.src && n.userData.srcTemplate && n.userData.srcTemplate.match(`{${id}}`) ){
let srcOldFragments = n.userData.src.replace(/.*#/,'')
let srcNewFragments = xrf.frag.src.expandURI( n ).replace(/.*#/,'') let srcNewFragments = xrf.frag.src.expandURI( n ).replace(/.*#/,'')
if( srcOldFragments != srcNewFragments ){ console.log(`URI.vars[${id}] => updating ${n.name} => ${srcNewFragments}`)
console.log(`URI.vars[${id}] => updating ${n.name}`)
let frag = xrf.hashbus.pub( srcNewFragments, n ) let frag = xrf.hashbus.pub( srcNewFragments, n )
} }
}
}) })
} }
}) })

View file

@ -35,6 +35,9 @@ xrf.frag.href = function(v, opts){
let click = mesh.userData.XRF.href.exec = (e) => { let click = mesh.userData.XRF.href.exec = (e) => {
// bubble up!
mesh.traverseAncestors( (n) => n.userData && n.userData.href && n.dispatchEvent({type:e.type,data:{}}) )
let lastPos = `pos=${camera.position.x.toFixed(2)},${camera.position.y.toFixed(2)},${camera.position.z.toFixed(2)}` let lastPos = `pos=${camera.position.x.toFixed(2)},${camera.position.y.toFixed(2)},${camera.position.z.toFixed(2)}`
xrf xrf
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree .emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
@ -46,19 +49,14 @@ xrf.frag.href = function(v, opts){
const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
// always commit current location in case of teleport (keep a trail of last positions before we navigate)
//if( isLocal && !hasPos ){
// xrf.hashbus.pub( v.string, xrf.model ) // publish to hashbus
//}else{
//if( !e.nocommit && !document.location.hash.match(lastPos) ) xrf.navigator.updateHash(`#${lastPos}`)
xrf.navigator.to(v.string) // let's surf xrf.navigator.to(v.string) // let's surf
//}
}) })
.catch( console.error ) .catch( console.error )
} }
let selected = mesh.userData.XRF.href.selected = (state) => () => { let selected = mesh.userData.XRF.href.selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
console.log("state="+(selected?'selected':'unselected'))
xrf.interactive.objects.map( (o) => { xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false let newState = o.name == mesh.name ? state : false
if( o.material ){ if( o.material ){

View file

@ -3,7 +3,7 @@ xrf.frag.loop = function(v, opts){
// handle object media players // handle object media players
if( mesh && mesh.media ){ if( mesh && mesh.media ){
for( let i in mesh.media ) mesh.media[i].pub(v) for( let i in mesh.media ) mesh.media[i].set("loop",v)
return return
} }

View file

@ -3,7 +3,7 @@ xrf.frag.s = function(v, opts){
// handle object media players // handle object media players
if( mesh && mesh.media ){ if( mesh && mesh.media ){
for( let i in mesh.media ) mesh.media[i].pub(v) for( let i in mesh.media ) mesh.media[i].set("s",v)
return return
} }

View file

@ -24,18 +24,18 @@ let loadAudio = (mimetype) => function(url,opts){
: new THREE.Audio( camera.listener ) : new THREE.Audio( camera.listener )
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
mesh.media.audio = { play: () => mesh.media.audio.autoplay = true } mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v }
audioLoader.load( url.replace(/#.*/,''), function( buffer ) { audioLoader.load( url.replace(/#.*/,''), function( buffer ) {
sound.setBuffer( buffer ); sound.setBuffer( buffer );
sound.setLoop(false); sound.setLoop(false);
sound.setVolume(1.0); sound.setVolume( 1.0 )
if( isPositionalAudio ){ if( isPositionalAudio ){
sound.setRefDistance( mesh.scale.x); sound.setRefDistance( mesh.scale.x);
sound.setRolloffFactor(20.0) sound.setRolloffFactor(20.0)
//sound.setDirectionalCone( 360, 360, 0.01 ); //sound.setDirectionalCone( 360, 360, 0.01 );
} }else sound.setVolume( mesh.scale.x )
mesh.add(sound) mesh.add(sound)
@ -44,13 +44,17 @@ let loadAudio = (mimetype) => function(url,opts){
sound[mediafragment] = v sound[mediafragment] = v
if( mediafragment == 't'){ if( mediafragment == 't'){
if( sound.isPlaying && v.y != undefined && v.x == v.y ){
sound.offset = v.x * buffer.sampleRate ;
sound.pause() sound.pause()
if( sound.isPlaying && v.y != undefined && v.x == v.y ) return return
}else sound.stop()
// apply embedded audio/video samplerate/fps or global mixer fps // apply embedded audio/video samplerate/fps or global mixer fps
sound.setLoopStart(v.x * buffer.sampleRate ); sound.setLoopStart(v.x);
sound.setLoopEnd(v.y * buffer.sampleRate ); sound.setLoopEnd(v.y || buffer.duration);
sound.offset = v.x * buffer.sampleRate ; sound.offset = v.x;
sound.play() sound.play()
} }
@ -66,25 +70,36 @@ let loadAudio = (mimetype) => function(url,opts){
sound.setLoop( v.loop ) sound.setLoop( v.loop )
sound.play() sound.play()
} }
debugger
}catch(e){ console.warn(e) } }catch(e){ console.warn(e) }
} }
// autoplay if user already requested play (before the sound was loaded) let lazySet = {}
let autoplay = mesh.media.audio && mesh.media.audio.autoplay let mediaFragments = ['t','loop','s']
mediaFragments.map( (f) => mesh.media.audio[f] && (lazySet[f] = mesh.media.audio[f]) )
mesh.media.audio = sound mesh.media.audio = sound
if( autoplay ){
xrf.hashbus.pub(mesh.media.audio.autoplay)
}
});
}
// autoplay if user already requested play (before the sound was loaded)
mediaFragments.map( (f) => {
if( lazySet[f] ) mesh.media.audio.set(f, lazySet[f] )
})
});
// apply Media fragments from URL
(['t','loop','s']).map( (f) => {
if( frag[f] ){
mesh.media.audio.set( f, frag[f] )
}
})
}
// stop playing audio when loading another scene // stop playing audio when loading another scene
xrf.addEventListener('reset', () => { xrf.addEventListener('reset', () => {
xrf.scene.traverse( (n) => n.audio && (n.audio.playXRF({x:0,y:0})) && (n.audio.remove()) ) xrf.scene.traverse( (n) => n.audio && (n.audio.playXRF({x:0,y:0})) && (n.audio.remove()) )
}) })
let audioMimeTypes = [ let audioMimeTypes = [
'audio/x-wav',
'audio/wav', 'audio/wav',
'audio/mpeg', 'audio/mpeg',
'audio/mp3', 'audio/mp3',

View file

@ -33,16 +33,19 @@ let loadVideo = (mimetype) => function(url,opts){
if( mediafragment == 't'){ if( mediafragment == 't'){
video.pause() video.pause()
if( t.x !== undefined && t.x == t.y ) return // stop paused if( v.x !== undefined && v.x == v.y ) return // stop paused
else{ else{
video.currentTime = t.x video.currentTime = v.x
video.time = t.x video.time = v.x
video.play() video.play()
} }
} }
if( mediafragment == 's' ){ if( mediafragment == 's' ){
video.playbackRate = Math.abs( video.speed ) // html5 video does not support reverseplay :/ video.playbackRate = Math.abs( video.speed ) // html5 video does not support reverseplay :/
} }
if( mediafragment == 'loop' ){
video.looping = true
}
} }
} }