first attempt

This commit is contained in:
Leon van Kammen 2023-11-08 18:28:18 +01:00
parent 8b4fde3571
commit e42fda84b4
25 changed files with 7129 additions and 6621 deletions

View File

@ -30,7 +30,7 @@
<textarea style="display:none"></textarea>
<canvas id="qrcode" style="display:none" width="300" height="300"></canvas>
<a-scene light="defaultLightsEnabled: false">
<a-scene renderer="colorManagement: true; highRefreshRate:true" light="defaultLightsEnabled: false">
<a-entity id="player" wasd-controls look-controls>
<a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity>
<a-entity id="left-hand" laser-controls="hand: left" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: #floor">
@ -57,34 +57,23 @@
window.embed = embed
window.notify('loading '+document.location.search.substr(1))
// reroute console messages to snackbar notifications
console.log = ( (log) => function(str){
if( str.match(/URL:/) ) str += "<br><br>(use back-button to go back)"
if( String(str).match(/:.*#/) ) window.notify(str)
log(str)
})(console.log)
let XRF = window.AFRAME.XRF
setupConsole( $('textarea') )
setupUrlBar( $('input#uri'), XRF )
// example listener, to prompt surfing to external asset
XRF.addEventListener('href',(e) => {
if( e.click ){
const { mesh, model, camera, scene, renderer, THREE} = e.XRF
const url = e.xrf.string
const isExtern = url[0] != '#'
const notIsHome = url != $('#home').getAttribute("xrf")
const promise = e.promise() // promisify event
document.querySelector('#model').setAttribute('href',url.replace(/.*\?/,'') )
if( !renderer.xr.isPresenting ){
if( isExtern && notIsHome ){
if( !confirm("teleport to "+url+" ?") ) return promise.reject('teleport rejected')
}
}
promise.resolve()
}
})
// on localhost enable debugging mode for developer convenience
let loc = document.location
if( loc.host.match(/^localhost/) ){
$('a-scene').setAttribute('stats')
XRF.debug = 1
}
// add screenshot component with camera to capture bigger size equirects
// document.querySelector('a-scene').components.screenshot.capture('perspective')

File diff suppressed because one or more lines are too long

View File

@ -267,7 +267,7 @@ export function notify(scope){
if( str.match(/error/g) ) opts.status = "danger"
if( str.match(/warning/g) ) opts.status = "warning"
}
opts = Object.assign({ message: str , status, timeout:2000 },opts)
opts = Object.assign({ message: str , status, timeout:4000 },opts)
SnackBar( opts )
}
}

View File

@ -12,18 +12,19 @@ XRWG.cleankey = (word) => String(word).replace(/[^0-9\.a-zA-Z_]/g,'')
XRWG.get = (v,k) => XRWG.find( (x) => x[ k || 'word'] == v )
XRWG.match = (str,types,level) => {
level = level || 1000
if( XRWG.length == 0 ) XRWG.generate(xrf)
level = level == undefined ? 1000 : level
types = types || []
let res = XRWG.filter( (n) => {
types.map( (type) => n[type] ? n = false : false )
return n
})
str = str.toLowerCase()
if( level <10 ) res = res.filter( (n) => n.key == str )
if( level <20 ) res = res.filter( (n) => n.word == str || n.key == str )
if( level <30 ) res = res.filter( (n) => n.word.match(str) || n.key == str )
if( level <40 ) res = res.filter( (n) => n.word.match(str) || n.key == str || String(n.value||'').match(str) )
if( level <1001 ) res = res.filter( (n) => n.word.match(str) != null || n.key.match(str) != null || String(n.value||'').match(str) != null)
if( level <10 ) res = res.filter( (n) => n.key == str )
if( level >=10 ) res = res.filter( (n) => n.word == str || n.key == str )
if( level >30 ) res = res.filter( (n) => n.word.match(str) || n.key == str )
if( level >40 ) res = res.filter( (n) => n.word.match(str) || n.key == str || String(n.value||'').match(str) )
if( level >999 ) res = res.filter( (n) => n.word.match(str) != null || n.key.match(str) != null || String(n.value||'').match(str) != null)
return res
}
@ -42,7 +43,7 @@ XRWG.generate = (opts) => {
node = { word: XRWG.cleankey(key), key, nodes:[spatialNode] }
if( spatialNode.userData[key] ) node.value = spatialNode.userData[key]
node[type] = true
xrf.emit('XRWG',node)
xrf.emit('XRWGnode',node)
XRWG.push( node )
}
}
@ -64,4 +65,5 @@ XRWG.generate = (opts) => {
// sort by n
XRWG.sort( (a,b) => a.nodes.length - b.nodes.length )
XRWG = XRWG.reverse() // the cleankey/get functions e.g. will persist
xrf.emit('XRWG',XRWG)
}

View File

@ -3,6 +3,11 @@ window.AFRAME.registerComponent('xrf', {
},
init: function () {
if( !AFRAME.XRF ){
// start with black
document.querySelector('[camera]').setAttribute('xrf-fade','')
AFRAME.fade = document.querySelector('[camera]').components['xrf-fade']
document.querySelector('a-scene').addEventListener('loaded', () => {
// enable XR fragments
@ -12,7 +17,6 @@ window.AFRAME.registerComponent('xrf', {
camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
debug: true,
loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
glb: THREE.GLTFLoader
@ -20,6 +24,28 @@ window.AFRAME.registerComponent('xrf', {
})
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
xrf.addEventListener('navigateLoaded', () => setTimeout( () => AFRAME.fade.out(),500) )
xrf.addEventListener('href', (opts) => {
if( opts.click){
console.dir(opts)
let p = opts.promise()
if( opts.xrf.string[0] == '#' ){ // local teleport // local teleport // local teleport // local teleport
let fastFadeMs = 200
AFRAME.fade.in(fastFadeMs)
setTimeout( () => {
p.resolve()
AFRAME.fade.out(fastFadeMs)
}, fastFadeMs)
}else{
AFRAME.fade.in()
setTimeout( () => {
p.resolve()
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
}, AFRAME.fade.data.fadetime )
}
}
})
// in order to set the rotation programmatically
// we need to disable look-controls
xrf.rot = (xrf,v,opts) => {
@ -40,9 +66,11 @@ window.AFRAME.registerComponent('xrf', {
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
el.setAttribute("class","ray") // expose to raycaster
el.setAttribute("pressable", '') // detect hand-controller click
// add click
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
el.addEventListener("click", clickHandler )
//el.addEventListener("pressedstarted", 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 )

View File

@ -0,0 +1,27 @@
AFRAME.registerComponent('xrf-fade', {
schema:{
fadetime:{type:"number", default: 1000},
color:{type:"color", default:"black"},
opacity:{type:"float",default:1.0}
},
init: function(){
let fb = this.fb = document.createElement("a-box")
fb.setAttribute("scale", "1 1 1")
fb.setAttribute("material", `color: ${this.data.color}; transparent: true; side: back; shader: flat; opacity:1`)
this.el.appendChild(fb)
},
out: function(fadetime){
if( fadetime != undefined ) this.data.fadetime = fadetime
if( this.data.opacity == 0 ) return
this.data.opacity = 0.0
this.fb.setAttribute("animation", `property: components.material.material.opacity; dur: ${this.data.fadetime}; from: 1; to: ${this.data.opacity}`)
setTimeout( () => this.fb.object3D.visible = false, this.data.fadetime )
},
"in": function(fadetime){
if( fadetime != undefined ) this.data.fadetime = fadetime
if( this.data.opacity == 1 ) return
this.data.opacity = 1.0
this.fb.object3D.visible = true
this.fb.setAttribute("animation", `property: components.material.material.opacity; dur: ${this.data.fadetime}; from: 0; to: ${this.data.opacity}`)
}
});

View File

@ -4,7 +4,8 @@
var xrf = {}
xrf.init = function(opts){
opts = opts || {}
opts = opts || {}
xrf.debug = parseInt( ( document.location.hash.match(/debug=([0-9])/) || [0,'0'] )[1] )
xrf.Parser.debug = xrf.debug
xrf.detectCameraRig(opts)
for ( let i in opts ) xrf[i] = opts[i]

View File

@ -1,5 +1,5 @@
/*
* (promise-able) EVENTS
* (promise-able) EVENTS (optionally continue after listeners are finished using .then)
*
* example:
*
@ -24,14 +24,23 @@ xrf.addEventListener = function(eventName, callback, scene) {
// add the callback to the listeners array for this event name
this._listeners[eventName].push(callback);
return () => {
console.log("size = "+this._listeners[eventName].length)
this._listeners[eventName] = this._listeners[eventName].filter( (c) => c != callback )
console.log("size = "+this._listeners[eventName].length)
}
};
xrf.emit = function(eventName, data){
if( typeof data != 'object' ) throw 'emit() requires passing objects'
if( xrf.debug && ( eventName != "render" || xrf.debug == eventName ) ){
let label = String(`xrf.emit('${eventName}')`).padEnd(35," ");
label += data.mesh && data.mesh.name ? '#'+data.mesh.name : ''
console.groupCollapsed(label)
console.info(data)
console.groupEnd(label)
if( xrf.debug > 1 ) debugger
}
// forward to THREEjs eventbus if any
if( data.scene ) data.scene.dispatchEvent( eventName, data )
if( data.mesh ) data.mesh.dispatchEvent( eventName, data )
return xrf.emit.promise(eventName,data)
}
@ -46,15 +55,21 @@ xrf.emit.normal = function(eventName, data) {
};
xrf.emit.promise = function(e, opts){
opts.XRF = xrf // always pass root XRF obj
return new Promise( (resolve, reject) => {
opts.promise = () => {
opts.promise.halted = true
return { resolve, reject }
opts.promises = opts.promises || []
opts.promises.push(0)
return {
resolve: ((index) => () => {
opts.promises[index] = 1
let succesful = opts.promises.reduce( (a,b) => a+b )
if( succesful == opts.promises.length ) resolve(opts)
})(opts.promises.length-1),
reject: console.error
}
}
xrf.emit.normal(e, opts)
delete opts.XRF
if( !opts.promise.halted ) resolve()
if( !opts.promises ) resolve(opts)
delete opts.promise
})
}

File diff suppressed because one or more lines are too long

View File

@ -26,7 +26,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
for( let k in frag ){
let opts = {frag, mesh, model, camera: xrf.camera, scene: model.scene, renderer: xrf.renderer, THREE: xrf.THREE, hashbus: xrf.hashbus }
mesh.userData.XRF = frag // allow fragment impl to access XRF obj already
xrf.emit('mesh',opts)
xrf.emit('frag2mesh',opts)
.then( () => pub.fragment(k,opts) )
}
}
@ -39,8 +39,8 @@ pub.fragment = (k, opts ) => { // evaluate one fragment
xrf.emit(k,opts)
.then( () => {
let func = xrf.frag[k] || function(){}
if( xrf[k] ) xrf[k]( func, frag, opts)
else func( frag, opts)
if( typeof xrf[k] == 'function' ) xrf[k]( func, frag, opts)
else func( frag, opts)
})
}

View File

@ -25,7 +25,7 @@ xrf.patchRenderer = function(opts){
// update clock
let time = xrf.clock.getDelta()
// allow entities to do stuff during render (onBeforeRender and onAfterRender don't always fire)
xrf.emit('render',{scene,camera,time}) // allow fragments to do something at renderframe
xrf.emit('render',{scene,camera,time,render}) // allow fragments to do something at renderframe
render(scene,camera)
})(renderer.render.bind(renderer))
@ -53,7 +53,6 @@ xrf.parseModel = function(model,url){
model.file = file
// eval embedded XR fragments
model.scene.traverse( (mesh) => {
mesh.renderOrder = 2 // render after stencil buffers
xrf.hashbus.pub.mesh(mesh,model)
})
model.animations.map( (a) => console.log("anim: "+a.name) )
@ -83,10 +82,7 @@ xrf.reset = () => {
xrf.layers = 0
xrf.emit('reset',{})
// remove mixers
xrf.mixers.map( (m) => {
m.stop()
delete m
})
xrf.mixers.map( (m) => m.stop())
xrf.mixers = []
}

View File

@ -4,6 +4,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
if( !url ) throw 'xrf.navigator.to(..) no url given'
let hashbus = xrf.hashbus
xrf.emit('navigate', {url,loader,data})
return new Promise( (resolve,reject) => {
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
@ -43,6 +44,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
},2000)
xrf.add( model.scene )
xrf.navigator.updateHash(hash)
xrf.emit('navigateLoaded',{url,model})
resolve(model)
}

View File

@ -0,0 +1,74 @@
xrf.portalNonEuclidian = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
let toFrag = xrf.URI.parse( v.string )
// turn plane into stencilplane
mesh.material = new THREE.MeshPhongMaterial({ color: 'green' });
mesh.material.depthWrite = false;
mesh.material.stencilWrite = true;
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
//mesh.material.side = THREE.DoubleSide (this requires flipping normals based on camerapos)
mesh.stencilRef = xrf.portalNonEuclidian.stencilRef
let stencilPos = new xrf.THREE.Vector3()
mesh.getWorldPosition(stencilPos)
// allow objects to flip between original and stencil position (which puts them behind stencilplane)
const addStencilFeature = (n) => {
n.stencil = (
(pos,stencilPos, stencilMaterial, material ) => (enabled) => {
if( mesh.stencilScene.active ) enabled = false // always deactive when stencil was clicked
let sRef = enabled ? mesh.stencilRef : 0
n.position.copy( enabled ? stencilPos : pos )
xrf.portalNonEuclidian.selectStencil(n, sRef )
n.traverse( (c) => xrf.portalNonEuclidian.selectStencil(c,sRef) )
}
)( n.position.clone(), stencilPos, n.material.clone, n.material )
return n
}
// collect related objects from XRWG to render inside stencilplane
let objs = XRWG.match(mesh.name,0)
if( objs.length == 0 ) return console.warn(`no objects are tagged with (portal)object name '${mesh.name}'`)
objs = objs[0].nodes
.filter( (n) => n.uuid != mesh.uuid && !n.stencilRef ) // filter out stencilplane
.map(addStencilFeature)
// put it into a scene so we can render it separately
mesh.stencilScene = new xrf.THREE.Scene()
mesh.stencilScene.children = objs
// enable the stencil-material of the stencil objects
mesh.onAfterRender = function(){
mesh.stencilScene.traverse( (n) => n.stencil ? n.stencil(true) : false )
}
xrf.addEventListener('href', (opts) => {
if( opts.click && opts.mesh.uuid == mesh.uuid){
// reposition stencilScene objects back to their original (and disable the stencil material)
mesh.stencilScene.traverse( (n) => n.stencil ? n.stencil(false) : false )
mesh.stencilScene.active = true
}else mesh.stencilScene.active = false
})
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
console.log("enabling portal for object '${mesh.name}'`")
}
xrf.portalNonEuclidian.selectStencil = (n, stencilRef, material) => {
if( n.material ){
if( !n.stencilMaterial ){
n.originalMaterial = n.material
n.stencilMaterial = n.material.clone()
n.stencilMaterial.stencilRef = stencilRef
n.stencilMaterial.stencilWrite = stencilRef
n.stencilMaterial.stencilFunc = xrf.THREE.EqualStencilFunc;
}
n.material = stencilRef > 0 ? n.stencilMaterial : n.originalMaterial
}
}
xrf.portalNonEuclidian.stencilRef = 1

View File

@ -17,6 +17,7 @@ xrf.addEventListener('dynamicKey', (opts) => {
xrf.drawLineToMesh = (opts) => {
let {scene,mesh,frag,id} = opts
const THREE = xrf.THREE
let oldSelection
// Selection of Interest if predefined_view matches object name
if( mesh.visible && mesh.material){
@ -35,8 +36,9 @@ xrf.drawLineToMesh = (opts) => {
return center;
}
xrf.camera.getCam().updateMatrixWorld(true); // always keeps me diving into the docs :]
xrf.camera.getCam().getWorldPosition(from)
let cam = xrf.camera.getCam ? xrf.camera.getCam() : xrf.camera // *FIXME* camerarig/rig are conflicting
cam.updateMatrixWorld(true); // always keeps me diving into the docs :]
cam.getWorldPosition(from)
from.y = 0.5 // originate from the heart chakra! :p
const points = [from, getCenterPoint(mesh) ]
const geometry = new THREE.BufferGeometry().setFromPoints( points );

View File

@ -41,7 +41,6 @@ xrf.frag.href = function(v, opts){
let click = mesh.userData.XRF.href.exec = (e) => {
let lastPos = `pos=${camera.position.x.toFixed(2)},${camera.position.y.toFixed(2)},${camera.position.z.toFixed(2)}`
xrf
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
.then( () => {
@ -60,7 +59,12 @@ xrf.frag.href = function(v, opts){
let newState = o.name == mesh.name ? state : false
if( o.material ){
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
//if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
if( o.material.emissive ){
if( !o.material.emissive.original ) o.material.emissive.original = o.material.emissive.clone()
o.material.emissive.r = o.material.emissive.g = o.material.emissive.b =
newState ? o.material.emissive.original.r + 0.2 : o.material.emissive.original.r
}
}
})
// update mouse cursor
@ -75,9 +79,10 @@ xrf.frag.href = function(v, opts){
mesh.addEventListener('click', click )
mesh.addEventListener('mousemove', selected(true) )
mesh.addEventListener('mouseenter', selected(true) )
mesh.addEventListener('mouseleave', selected(false) )
if( isLocal && isPlane && !hasSrc ) xrf.portalNonEuclidian(v,opts)
if( isLocal && isPlane && !hasSrc && !mesh.material.map ) xrf.portalNonEuclidian(v,opts)
// lazy add mesh (because we're inside a recursive traverse)
setTimeout( (mesh) => {

View File

@ -1,41 +0,0 @@
xrf.portalNonEuclidian = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
let toFrag = xrf.URI.parse( v.string )
// turn plane into stencilplane
mesh.material = new THREE.MeshPhongMaterial({ color: 'green' });
mesh.material.depthWrite = false;
mesh.material.stencilWrite = true;
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
mesh.cam = xrf.camera.getCam().clone()
mesh.cam.position.x = toFrag.pos.x
mesh.cam.position.y = toFrag.pos.y + 1
mesh.cam.position.z = toFrag.pos.z
mesh.isStencil = true
mesh.onBeforeRender = function( renderer, scene, camera, geometry, material, group ){
xrf.scene.traverse( (n) => !n.isStencil && n.material ? n.material.stencilRef = this.material.stencilRef : false )
xrf.renderer.render( xrf.scene, this.cam )
}
mesh.onAfterRender = function( renderer, scene, camera, geometry, material, group ){
xrf.scene.traverse( (n) => !n.isStencil && n.material ? n.material.stencilRef = 0 : false )
}
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
// OBJECT INSIDE CUBE
//objectMat.stencilWrite = true;
//objectMat.stencilRef = stencilRef;
//objectMat.stencilFunc = THREE.EqualStencilFunc;
//const object = new THREE.Mesh(objectGeom, objectMat);
//scene.add(object);
console.log("enabling stencil plane")
}
xrf.portalNonEuclidian.stencilRef = 1

View File

@ -1,7 +1,6 @@
// *TODO* use webgl instancing
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts

View File

@ -7,14 +7,15 @@
*/
let loadAudio = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts
let {mesh,src,camera,THREE} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let frag = xrf.URI.parse( url )
/* WebAudio: setup context via THREEjs */
if( !camera.listener ){
camera.listener = new THREE.AudioListener();
camera.getCam().add( camera.listener );
// *FIXME* camera vs camerarig conflict
(camera.getCam ? camera.getCam() : camera).add( camera.listener );
}
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)

View File

@ -5,7 +5,7 @@
*/
xrf.frag.src.type['image/png'] = function(url,opts){
let {mesh} = opts
let {mesh,THREE} = opts
let restrictTo3DBoundingBox = mesh.geometry
let renderEquirect = (texture) => {
@ -73,7 +73,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
}
}
//const geometry = new THREE.BoxGeometry();
mesh.material = new THREE.MeshBasicMaterial({
mesh.material = new xrf.THREE.MeshBasicMaterial({
map: texture,
transparent: url.match(/(png|gif)/) ? true : false,
side: THREE.DoubleSide,

View File

@ -2,6 +2,7 @@
let loadVideo = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
const THREE = xrf.THREE
let frag = xrf.URI.parse( url )
let video = mesh.video = document.createElement('video')

View File

@ -1,14 +1,14 @@
xrf.addEventListener('env', (opts) => {
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
if( frag.env && !scene.environment ){
let env = scene.getObjectByName(frag.env.string)
if( !env ) env = xrf.scene.getObjectByName(frag.env.string) // repurpose from parent scene
if( !env ) return console.warn("xrf.env "+frag.env.string+" not found")
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = env.material.map
//let env = scene.getObjectByName(frag.env.string)
//if( !env ) env = xrf.scene.getObjectByName(frag.env.string) // repurpose from parent scene
//if( !env ) return console.warn("xrf.env "+frag.env.string+" not found")
//env.material.map.mapping = THREE.EquirectangularReflectionMapping;
//scene.environment = env.material.map
//scene.texture = env.material.map
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 2;
// renderer.toneMapping = THREE.ACESFilmicToneMapping;
// renderer.toneMappingExposure = 2;
console.log(` └ applied image '${frag.env.string}' as environment map`)
}

View File

@ -1,6 +1,6 @@
xrf.macros = {}
xrf.addEventListener('mesh', (opts) => {
xrf.addEventListener('frag2mesh', (opts) => {
let { frag, mesh, model, camera, scene, renderer, THREE, hashbus} = opts
for( let k in frag ){

14
test/aframe/index.html Normal file
View File

@ -0,0 +1,14 @@
<html lang="en">
<head>
<title>AFRAME - xrfragment sandbox</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
<script src="./../../dist/xrfragment.aframe.js"></script>
<script src="./index.js"></script>
<script src="./pubsub.js"></script>
</head>
<body>
<h3>open the browserconsole to see the results</h3>
</body>
</html>

23
test/aframe/index.js Normal file
View File

@ -0,0 +1,23 @@
// in the browser use this instead of require():
//
// <script src="dist/xrfragment.js"></script>
// <script>
// var XRF = xrfragment;
// </script>
console.assert = ((assert) => (a,b) => {
console.log('♥ test: '+b.reason)
assert.call( console, a, b )
})(console.assert)
/*
* parser checks
*/
let frags = xrf.URI.parse('://foo.com/1.gltf#pos=1.0,2.0,3.0&q=-.foo&t=1,100',true)
console.assert( frags.t, {frags, reason:'URI.parse(): t needs to be set'})
let q = new xrf.Query();
console.assert( new xrf.Query(), {reason: 'new Query() should be available'})

38
test/aframe/pubsub.js Normal file
View File

@ -0,0 +1,38 @@
// test simple event
opts = {a:0}
xrf.addEventListener('foo1', (opts) => opts.a = 1 )
xrf.emit('foo1',opts)
setTimeout( () => console.assert( opts.a, {opts, reason:"xrf.emit('foo1',...) should set a to 1"}), 100)
// test simple promise event
opts = {a:0}
xrf.addEventListener('foo2', (e) => {
let p = e.promise()
e.a = 1
p.resolve(e)
})
xrf.emit('foo2',opts)
.then( (opts) => {
console.assert( opts.a, {opts, reason:"xrf.emit('foo2',...) should set a to 1 via promise"})
})
// test multiple promise event
opts = {a:""}
xrf.addEventListener('foo3', (e) => {
let p = e.promise()
setTimeout( () => {
e.a += "1"
p.resolve(e)
}, 100 )
})
xrf.addEventListener('foo3', (e) => {
let p = e.promise()
e.a += "2"
p.resolve(e)
})
xrf.emit('foo3',opts)
.then( (opts) => {
opts.a += "3"
console.assert( opts.a == "213", {opts, reason:"xrf.emit('foo3',...) should support multiple promise listeners"})
})