update documentation
This commit is contained in:
parent
723e7572cc
commit
72f1e69aad
|
@ -802,6 +802,9 @@ xrf.frag = {}
|
|||
xrf.model = {}
|
||||
|
||||
xrf.init = ((init) => function(opts){
|
||||
let scene = new opts.THREE.Group()
|
||||
opts.scene.add(scene)
|
||||
opts.scene = scene
|
||||
init(opts)
|
||||
if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
|
||||
|
||||
|
@ -854,7 +857,7 @@ xrf.parseModel = function(model,url){
|
|||
model.mixer.update( model.clock.getDelta() )
|
||||
|
||||
// update focusline
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime() ))/2
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime()*10 ))/2
|
||||
xrf.focusLine.material.dashSize = 0.2 + 0.02*Math.sin( model.clock.getElapsedTime() )
|
||||
xrf.focusLine.material.gapSize = 0.1 + 0.02*Math.sin( model.clock.getElapsedTime() *3 )
|
||||
xrf.focusLine.material.opacity = 0.25 + 0.15*Math.sin( model.clock.getElapsedTime() * 3 )
|
||||
|
@ -879,7 +882,8 @@ xrf.reset = () => {
|
|||
xrf.scene.traverse( (child) => child.isXRF ? nodes.push(child) : false )
|
||||
nodes.map( disposeObject ) // leave non-XRF objects intact
|
||||
xrf.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.add( xrf.interactive)
|
||||
xrf.add( xrf.interactive )
|
||||
xrf.layers = 0
|
||||
}
|
||||
|
||||
xrf.parseUrl = (url) => {
|
||||
|
@ -1333,7 +1337,7 @@ xrf.frag.fov = function(v, opts){
|
|||
|
||||
xrf.frag.href = function(v, opts){
|
||||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( mesh.userData.XRF.href.exec ) return // mesh already initialized
|
||||
|
||||
|
@ -1376,7 +1380,7 @@ xrf.frag.href = function(v, opts){
|
|||
varying float vDistance;
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition);
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition );
|
||||
vec2 sampleUV;
|
||||
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
|
||||
|
@ -1384,7 +1388,7 @@ xrf.frag.href = function(v, opts){
|
|||
vec4 color = texture2D(pano, sampleUV);
|
||||
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
|
||||
float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
vec4 grayscale_color = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
gl_FragColor = grayscale_color;
|
||||
}
|
||||
`,
|
||||
|
@ -1395,14 +1399,15 @@ xrf.frag.href = function(v, opts){
|
|||
let click = mesh.userData.XRF.href.exec = (e) => {
|
||||
let isLocal = v.string[0] == '#'
|
||||
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( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1633,88 +1638,125 @@ xrf.frag.src = function(v, opts){
|
|||
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
|
||||
|
||||
console.log(" └ instancing src")
|
||||
let src = new THREE.Group()
|
||||
let frag = xrfragment.URI.parse(v.string)
|
||||
let src;
|
||||
let url = v.string
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
||||
|
||||
const localSRC = () => {
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
xrf.frag.src.eval( src, opts )
|
||||
mesh.add( src )
|
||||
const addScene = (scene,url,frag) => {
|
||||
src = xrf.frag.src.filterScene(scene,{...opts,frag})
|
||||
xrf.frag.src.scale( src, opts, url )
|
||||
xrf.frag.src.eval( src, opts, url )
|
||||
mesh.add(src)
|
||||
if( mesh.material ) mesh.material.visible = false
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
fetch(v.string, { method: 'HEAD' })
|
||||
const externalSRC = (url,frag,src) => {
|
||||
fetch(url, { method: 'HEAD' })
|
||||
.then( (res) => {
|
||||
console.log(`loading src ${v.string}`)
|
||||
console.log(`loading src ${url}`)
|
||||
let mimetype = res.headers.get('Content-type')
|
||||
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
|
||||
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
|
||||
console.log("src mimetype: "+mimetype)
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](v.string,opts) : xrf.frag.src.type.unknown(v.string,opts)
|
||||
opts = { ...opts, src, frag }
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
|
||||
})
|
||||
.finally( () => {
|
||||
.then( (model) => {
|
||||
if( model && model.scene ) addScene(model.scene, url, frag )
|
||||
})
|
||||
.finally( () => { })
|
||||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
if( url[0] == "#" ) addScene(scene,url,frag) // current file
|
||||
else externalSRC(url,frag) // external file
|
||||
}
|
||||
|
||||
xrf.frag.src.eval = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE, hashbus} = opts
|
||||
if( url ){
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
// scale URI XR Fragments (queries) inside src-value
|
||||
for( var i in frag ){
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
}
|
||||
hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
console.log(mesh.name+" url="+url)
|
||||
console.dir(mesh)
|
||||
//let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
//let frag = xrfragment.URI.parse(url)
|
||||
//// scale URI XR Fragments (queries) inside src-value
|
||||
//for( var i in frag ){
|
||||
// hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
//}
|
||||
//hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
//hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
}
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
xrf.frag.src.scale = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
if( restrictToBoundingBox ){
|
||||
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
if( restrictTo3DBoundingBox ){
|
||||
// spec 3 of https://xrfragment.org/#src
|
||||
// spec 1 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
// normalize instanced objectsize to boundingbox
|
||||
let bboxMesh = new THREE.Box3().setFromObject(mesh);
|
||||
let bboxScene = new THREE.Box3().setFromObject(scene);
|
||||
let maxScene = bboxScene.max.y > bboxScene.max.x ? bboxScene.max.y : bboxScene.max.x
|
||||
let maxMesh = bboxMesh.max.y > bboxMesh.max.x ? bboxMesh.max.y : bboxMesh.max.x
|
||||
let factor = maxMesh > maxScene ? maxScene / maxMesh : maxMesh / maxScene
|
||||
scene.scale.multiplyScalar( factor )
|
||||
let sizeFrom = new THREE.Vector3()
|
||||
let sizeTo = new THREE.Vector3()
|
||||
|
||||
let empty = new THREE.Object3D()
|
||||
|
||||
// *TODO* exclude invisible objects from boundingbox size-detection
|
||||
//
|
||||
// THREE.Box3.prototype.expandByObject = (function(expandByObject){
|
||||
// return function(object,precise){
|
||||
// return expandByObject.call(this, object.visible ? object : empty, precise)
|
||||
// }
|
||||
// })(THREE.Box3.prototype.expandByObject)
|
||||
|
||||
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
|
||||
new THREE.Box3().setFromObject(scene).getSize(sizeFrom)
|
||||
let ratio = sizeFrom.divide(sizeTo)
|
||||
scene.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z));
|
||||
// let factor = getMax(sizeTo) < getMax(sizeFrom) ? getMax(sizeTo) / getMax(sizeFrom) : getMax(sizeFrom) / getMax(sizeTo)
|
||||
// scene.scale.multiplyScalar( factor )
|
||||
}else{
|
||||
// spec 4 of https://xrfragment.org/#src
|
||||
// spec 2 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
console.log("normal scale: "+url)
|
||||
scene.scale.multiply( mesh.scale )
|
||||
}
|
||||
scene.isXRF = model.scene.isSRC = true
|
||||
if( !opts.recursive && mesh.material ) mesh.material.visible = false // lets hide the preview object because deleting disables animations+nested objs
|
||||
}
|
||||
|
||||
xrf.frag.src.filterScene = (scene,opts) => {
|
||||
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
|
||||
let obj, src
|
||||
console.dir(frag)
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
src = new THREE.Group()
|
||||
if( Object.keys(frag).length > 0 ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ){
|
||||
src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
}
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
}else src = scene.clone(true)
|
||||
console.dir({name: mesh.name, scene, frag})
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
return src
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1737,27 +1779,22 @@ xrf.frag.src.type['unknown'] = function( url, opts ){
|
|||
* mimetype: model/gltf+json
|
||||
*/
|
||||
|
||||
xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
||||
xrf.frag.src.type['gltf'] = function( url, opts ){
|
||||
return new Promise( (resolve,reject) => {
|
||||
let {mesh} = opts
|
||||
let {mesh,src} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let loader
|
||||
|
||||
const Loader = xrf.loaders[ext]
|
||||
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
|
||||
if( !dir.match("://") ){ // force relative path
|
||||
dir = dir[0] == '.' ? dir : `.${dir}`
|
||||
dir = dir[0] == './' ? dir : `./${dir}`
|
||||
loader = new Loader().setPath( dir )
|
||||
}else loader = new Loader()
|
||||
|
||||
const onLoad = (model) => {
|
||||
xrf.frag.src.scale( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
xrf.frag.src.eval( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
mesh.add( model.scene )
|
||||
loader.load(url, (model) => {
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
loader.load(url, onLoad )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1769,7 +1806,7 @@ xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
|||
|
||||
xrf.frag.src.type['image/png'] = function(url,opts){
|
||||
let {mesh} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
const texture = new THREE.TextureLoader().load( url );
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
|
|
|
@ -802,6 +802,9 @@ xrf.frag = {}
|
|||
xrf.model = {}
|
||||
|
||||
xrf.init = ((init) => function(opts){
|
||||
let scene = new opts.THREE.Group()
|
||||
opts.scene.add(scene)
|
||||
opts.scene = scene
|
||||
init(opts)
|
||||
if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
|
||||
|
||||
|
@ -854,7 +857,7 @@ xrf.parseModel = function(model,url){
|
|||
model.mixer.update( model.clock.getDelta() )
|
||||
|
||||
// update focusline
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime() ))/2
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime()*10 ))/2
|
||||
xrf.focusLine.material.dashSize = 0.2 + 0.02*Math.sin( model.clock.getElapsedTime() )
|
||||
xrf.focusLine.material.gapSize = 0.1 + 0.02*Math.sin( model.clock.getElapsedTime() *3 )
|
||||
xrf.focusLine.material.opacity = 0.25 + 0.15*Math.sin( model.clock.getElapsedTime() * 3 )
|
||||
|
@ -879,7 +882,8 @@ xrf.reset = () => {
|
|||
xrf.scene.traverse( (child) => child.isXRF ? nodes.push(child) : false )
|
||||
nodes.map( disposeObject ) // leave non-XRF objects intact
|
||||
xrf.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.add( xrf.interactive)
|
||||
xrf.add( xrf.interactive )
|
||||
xrf.layers = 0
|
||||
}
|
||||
|
||||
xrf.parseUrl = (url) => {
|
||||
|
@ -1333,7 +1337,7 @@ xrf.frag.fov = function(v, opts){
|
|||
|
||||
xrf.frag.href = function(v, opts){
|
||||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( mesh.userData.XRF.href.exec ) return // mesh already initialized
|
||||
|
||||
|
@ -1376,7 +1380,7 @@ xrf.frag.href = function(v, opts){
|
|||
varying float vDistance;
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition);
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition );
|
||||
vec2 sampleUV;
|
||||
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
|
||||
|
@ -1384,7 +1388,7 @@ xrf.frag.href = function(v, opts){
|
|||
vec4 color = texture2D(pano, sampleUV);
|
||||
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
|
||||
float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
vec4 grayscale_color = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
gl_FragColor = grayscale_color;
|
||||
}
|
||||
`,
|
||||
|
@ -1395,14 +1399,15 @@ xrf.frag.href = function(v, opts){
|
|||
let click = mesh.userData.XRF.href.exec = (e) => {
|
||||
let isLocal = v.string[0] == '#'
|
||||
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( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1633,88 +1638,125 @@ xrf.frag.src = function(v, opts){
|
|||
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
|
||||
|
||||
console.log(" └ instancing src")
|
||||
let src = new THREE.Group()
|
||||
let frag = xrfragment.URI.parse(v.string)
|
||||
let src;
|
||||
let url = v.string
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
||||
|
||||
const localSRC = () => {
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
xrf.frag.src.eval( src, opts )
|
||||
mesh.add( src )
|
||||
const addScene = (scene,url,frag) => {
|
||||
src = xrf.frag.src.filterScene(scene,{...opts,frag})
|
||||
xrf.frag.src.scale( src, opts, url )
|
||||
xrf.frag.src.eval( src, opts, url )
|
||||
mesh.add(src)
|
||||
if( mesh.material ) mesh.material.visible = false
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
fetch(v.string, { method: 'HEAD' })
|
||||
const externalSRC = (url,frag,src) => {
|
||||
fetch(url, { method: 'HEAD' })
|
||||
.then( (res) => {
|
||||
console.log(`loading src ${v.string}`)
|
||||
console.log(`loading src ${url}`)
|
||||
let mimetype = res.headers.get('Content-type')
|
||||
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
|
||||
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
|
||||
console.log("src mimetype: "+mimetype)
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](v.string,opts) : xrf.frag.src.type.unknown(v.string,opts)
|
||||
opts = { ...opts, src, frag }
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
|
||||
})
|
||||
.finally( () => {
|
||||
.then( (model) => {
|
||||
if( model && model.scene ) addScene(model.scene, url, frag )
|
||||
})
|
||||
.finally( () => { })
|
||||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
if( url[0] == "#" ) addScene(scene,url,frag) // current file
|
||||
else externalSRC(url,frag) // external file
|
||||
}
|
||||
|
||||
xrf.frag.src.eval = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE, hashbus} = opts
|
||||
if( url ){
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
// scale URI XR Fragments (queries) inside src-value
|
||||
for( var i in frag ){
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
}
|
||||
hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
console.log(mesh.name+" url="+url)
|
||||
console.dir(mesh)
|
||||
//let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
//let frag = xrfragment.URI.parse(url)
|
||||
//// scale URI XR Fragments (queries) inside src-value
|
||||
//for( var i in frag ){
|
||||
// hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
//}
|
||||
//hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
//hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
}
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
xrf.frag.src.scale = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
if( restrictToBoundingBox ){
|
||||
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
if( restrictTo3DBoundingBox ){
|
||||
// spec 3 of https://xrfragment.org/#src
|
||||
// spec 1 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
// normalize instanced objectsize to boundingbox
|
||||
let bboxMesh = new THREE.Box3().setFromObject(mesh);
|
||||
let bboxScene = new THREE.Box3().setFromObject(scene);
|
||||
let maxScene = bboxScene.max.y > bboxScene.max.x ? bboxScene.max.y : bboxScene.max.x
|
||||
let maxMesh = bboxMesh.max.y > bboxMesh.max.x ? bboxMesh.max.y : bboxMesh.max.x
|
||||
let factor = maxMesh > maxScene ? maxScene / maxMesh : maxMesh / maxScene
|
||||
scene.scale.multiplyScalar( factor )
|
||||
let sizeFrom = new THREE.Vector3()
|
||||
let sizeTo = new THREE.Vector3()
|
||||
|
||||
let empty = new THREE.Object3D()
|
||||
|
||||
// *TODO* exclude invisible objects from boundingbox size-detection
|
||||
//
|
||||
// THREE.Box3.prototype.expandByObject = (function(expandByObject){
|
||||
// return function(object,precise){
|
||||
// return expandByObject.call(this, object.visible ? object : empty, precise)
|
||||
// }
|
||||
// })(THREE.Box3.prototype.expandByObject)
|
||||
|
||||
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
|
||||
new THREE.Box3().setFromObject(scene).getSize(sizeFrom)
|
||||
let ratio = sizeFrom.divide(sizeTo)
|
||||
scene.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z));
|
||||
// let factor = getMax(sizeTo) < getMax(sizeFrom) ? getMax(sizeTo) / getMax(sizeFrom) : getMax(sizeFrom) / getMax(sizeTo)
|
||||
// scene.scale.multiplyScalar( factor )
|
||||
}else{
|
||||
// spec 4 of https://xrfragment.org/#src
|
||||
// spec 2 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
console.log("normal scale: "+url)
|
||||
scene.scale.multiply( mesh.scale )
|
||||
}
|
||||
scene.isXRF = model.scene.isSRC = true
|
||||
if( !opts.recursive && mesh.material ) mesh.material.visible = false // lets hide the preview object because deleting disables animations+nested objs
|
||||
}
|
||||
|
||||
xrf.frag.src.filterScene = (scene,opts) => {
|
||||
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
|
||||
let obj, src
|
||||
console.dir(frag)
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
src = new THREE.Group()
|
||||
if( Object.keys(frag).length > 0 ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ){
|
||||
src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
}
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
}else src = scene.clone(true)
|
||||
console.dir({name: mesh.name, scene, frag})
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
return src
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1737,27 +1779,22 @@ xrf.frag.src.type['unknown'] = function( url, opts ){
|
|||
* mimetype: model/gltf+json
|
||||
*/
|
||||
|
||||
xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
||||
xrf.frag.src.type['gltf'] = function( url, opts ){
|
||||
return new Promise( (resolve,reject) => {
|
||||
let {mesh} = opts
|
||||
let {mesh,src} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let loader
|
||||
|
||||
const Loader = xrf.loaders[ext]
|
||||
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
|
||||
if( !dir.match("://") ){ // force relative path
|
||||
dir = dir[0] == '.' ? dir : `.${dir}`
|
||||
dir = dir[0] == './' ? dir : `./${dir}`
|
||||
loader = new Loader().setPath( dir )
|
||||
}else loader = new Loader()
|
||||
|
||||
const onLoad = (model) => {
|
||||
xrf.frag.src.scale( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
xrf.frag.src.eval( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
mesh.add( model.scene )
|
||||
loader.load(url, (model) => {
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
loader.load(url, onLoad )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1769,7 +1806,7 @@ xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
|||
|
||||
xrf.frag.src.type['image/png'] = function(url,opts){
|
||||
let {mesh} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
const texture = new THREE.TextureLoader().load( url );
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
|
|
|
@ -802,6 +802,9 @@ xrf.frag = {}
|
|||
xrf.model = {}
|
||||
|
||||
xrf.init = ((init) => function(opts){
|
||||
let scene = new opts.THREE.Group()
|
||||
opts.scene.add(scene)
|
||||
opts.scene = scene
|
||||
init(opts)
|
||||
if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
|
||||
|
||||
|
@ -854,7 +857,7 @@ xrf.parseModel = function(model,url){
|
|||
model.mixer.update( model.clock.getDelta() )
|
||||
|
||||
// update focusline
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime() ))/2
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime()*10 ))/2
|
||||
xrf.focusLine.material.dashSize = 0.2 + 0.02*Math.sin( model.clock.getElapsedTime() )
|
||||
xrf.focusLine.material.gapSize = 0.1 + 0.02*Math.sin( model.clock.getElapsedTime() *3 )
|
||||
xrf.focusLine.material.opacity = 0.25 + 0.15*Math.sin( model.clock.getElapsedTime() * 3 )
|
||||
|
@ -879,7 +882,8 @@ xrf.reset = () => {
|
|||
xrf.scene.traverse( (child) => child.isXRF ? nodes.push(child) : false )
|
||||
nodes.map( disposeObject ) // leave non-XRF objects intact
|
||||
xrf.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.add( xrf.interactive)
|
||||
xrf.add( xrf.interactive )
|
||||
xrf.layers = 0
|
||||
}
|
||||
|
||||
xrf.parseUrl = (url) => {
|
||||
|
@ -1333,7 +1337,7 @@ xrf.frag.fov = function(v, opts){
|
|||
|
||||
xrf.frag.href = function(v, opts){
|
||||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( mesh.userData.XRF.href.exec ) return // mesh already initialized
|
||||
|
||||
|
@ -1376,7 +1380,7 @@ xrf.frag.href = function(v, opts){
|
|||
varying float vDistance;
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition);
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition );
|
||||
vec2 sampleUV;
|
||||
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
|
||||
|
@ -1384,7 +1388,7 @@ xrf.frag.href = function(v, opts){
|
|||
vec4 color = texture2D(pano, sampleUV);
|
||||
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
|
||||
float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
vec4 grayscale_color = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
gl_FragColor = grayscale_color;
|
||||
}
|
||||
`,
|
||||
|
@ -1395,14 +1399,15 @@ xrf.frag.href = function(v, opts){
|
|||
let click = mesh.userData.XRF.href.exec = (e) => {
|
||||
let isLocal = v.string[0] == '#'
|
||||
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( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1633,88 +1638,125 @@ xrf.frag.src = function(v, opts){
|
|||
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
|
||||
|
||||
console.log(" └ instancing src")
|
||||
let src = new THREE.Group()
|
||||
let frag = xrfragment.URI.parse(v.string)
|
||||
let src;
|
||||
let url = v.string
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
||||
|
||||
const localSRC = () => {
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
xrf.frag.src.eval( src, opts )
|
||||
mesh.add( src )
|
||||
const addScene = (scene,url,frag) => {
|
||||
src = xrf.frag.src.filterScene(scene,{...opts,frag})
|
||||
xrf.frag.src.scale( src, opts, url )
|
||||
xrf.frag.src.eval( src, opts, url )
|
||||
mesh.add(src)
|
||||
if( mesh.material ) mesh.material.visible = false
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
fetch(v.string, { method: 'HEAD' })
|
||||
const externalSRC = (url,frag,src) => {
|
||||
fetch(url, { method: 'HEAD' })
|
||||
.then( (res) => {
|
||||
console.log(`loading src ${v.string}`)
|
||||
console.log(`loading src ${url}`)
|
||||
let mimetype = res.headers.get('Content-type')
|
||||
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
|
||||
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
|
||||
console.log("src mimetype: "+mimetype)
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](v.string,opts) : xrf.frag.src.type.unknown(v.string,opts)
|
||||
opts = { ...opts, src, frag }
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
|
||||
})
|
||||
.finally( () => {
|
||||
.then( (model) => {
|
||||
if( model && model.scene ) addScene(model.scene, url, frag )
|
||||
})
|
||||
.finally( () => { })
|
||||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
if( url[0] == "#" ) addScene(scene,url,frag) // current file
|
||||
else externalSRC(url,frag) // external file
|
||||
}
|
||||
|
||||
xrf.frag.src.eval = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE, hashbus} = opts
|
||||
if( url ){
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
// scale URI XR Fragments (queries) inside src-value
|
||||
for( var i in frag ){
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
}
|
||||
hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
console.log(mesh.name+" url="+url)
|
||||
console.dir(mesh)
|
||||
//let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
//let frag = xrfragment.URI.parse(url)
|
||||
//// scale URI XR Fragments (queries) inside src-value
|
||||
//for( var i in frag ){
|
||||
// hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
//}
|
||||
//hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
//hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
}
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
xrf.frag.src.scale = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
if( restrictToBoundingBox ){
|
||||
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
if( restrictTo3DBoundingBox ){
|
||||
// spec 3 of https://xrfragment.org/#src
|
||||
// spec 1 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
// normalize instanced objectsize to boundingbox
|
||||
let bboxMesh = new THREE.Box3().setFromObject(mesh);
|
||||
let bboxScene = new THREE.Box3().setFromObject(scene);
|
||||
let maxScene = bboxScene.max.y > bboxScene.max.x ? bboxScene.max.y : bboxScene.max.x
|
||||
let maxMesh = bboxMesh.max.y > bboxMesh.max.x ? bboxMesh.max.y : bboxMesh.max.x
|
||||
let factor = maxMesh > maxScene ? maxScene / maxMesh : maxMesh / maxScene
|
||||
scene.scale.multiplyScalar( factor )
|
||||
let sizeFrom = new THREE.Vector3()
|
||||
let sizeTo = new THREE.Vector3()
|
||||
|
||||
let empty = new THREE.Object3D()
|
||||
|
||||
// *TODO* exclude invisible objects from boundingbox size-detection
|
||||
//
|
||||
// THREE.Box3.prototype.expandByObject = (function(expandByObject){
|
||||
// return function(object,precise){
|
||||
// return expandByObject.call(this, object.visible ? object : empty, precise)
|
||||
// }
|
||||
// })(THREE.Box3.prototype.expandByObject)
|
||||
|
||||
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
|
||||
new THREE.Box3().setFromObject(scene).getSize(sizeFrom)
|
||||
let ratio = sizeFrom.divide(sizeTo)
|
||||
scene.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z));
|
||||
// let factor = getMax(sizeTo) < getMax(sizeFrom) ? getMax(sizeTo) / getMax(sizeFrom) : getMax(sizeFrom) / getMax(sizeTo)
|
||||
// scene.scale.multiplyScalar( factor )
|
||||
}else{
|
||||
// spec 4 of https://xrfragment.org/#src
|
||||
// spec 2 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
console.log("normal scale: "+url)
|
||||
scene.scale.multiply( mesh.scale )
|
||||
}
|
||||
scene.isXRF = model.scene.isSRC = true
|
||||
if( !opts.recursive && mesh.material ) mesh.material.visible = false // lets hide the preview object because deleting disables animations+nested objs
|
||||
}
|
||||
|
||||
xrf.frag.src.filterScene = (scene,opts) => {
|
||||
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
|
||||
let obj, src
|
||||
console.dir(frag)
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
src = new THREE.Group()
|
||||
if( Object.keys(frag).length > 0 ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ){
|
||||
src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
}
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
}else src = scene.clone(true)
|
||||
console.dir({name: mesh.name, scene, frag})
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
return src
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1737,27 +1779,22 @@ xrf.frag.src.type['unknown'] = function( url, opts ){
|
|||
* mimetype: model/gltf+json
|
||||
*/
|
||||
|
||||
xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
||||
xrf.frag.src.type['gltf'] = function( url, opts ){
|
||||
return new Promise( (resolve,reject) => {
|
||||
let {mesh} = opts
|
||||
let {mesh,src} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let loader
|
||||
|
||||
const Loader = xrf.loaders[ext]
|
||||
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
|
||||
if( !dir.match("://") ){ // force relative path
|
||||
dir = dir[0] == '.' ? dir : `.${dir}`
|
||||
dir = dir[0] == './' ? dir : `./${dir}`
|
||||
loader = new Loader().setPath( dir )
|
||||
}else loader = new Loader()
|
||||
|
||||
const onLoad = (model) => {
|
||||
xrf.frag.src.scale( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
xrf.frag.src.eval( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
mesh.add( model.scene )
|
||||
loader.load(url, (model) => {
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
loader.load(url, onLoad )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1769,7 +1806,7 @@ xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
|||
|
||||
xrf.frag.src.type['image/png'] = function(url,opts){
|
||||
let {mesh} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
const texture = new THREE.TextureLoader().load( url );
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
|
|
|
@ -93,12 +93,12 @@ XR Fragments allows us to enrich existing dataformats, by recursive use of exist
|
|||
<p>How can we add more features to existing text & 3D scenes, without introducing new dataformats?<br>
|
||||
Historically, there’s many attempts to create the ultimate markuplanguage or 3D fileformat.<br>
|
||||
Their lowest common denominator is: (co)authoring using plain text.<br>
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by recursive use of existing technologies:<br></p>
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:<br></p>
|
||||
|
||||
<ol>
|
||||
<li>addressibility and navigation of 3D scenes/objects: <a href="https://en.wikipedia.org/wiki/URI_fragment">URI Fragments</a> + src/href spatial metadata</li>
|
||||
<li>Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) (and augmenting text with <a href="https://github.com/coderofsalvation/tagbibs">bibs</a> / <a href="https://en.wikipedia.org/wiki/BibTeX">BibTags</a> appendices (see <a href="https://visual-meta.info">visual-meta</a> e.g.)</li>
|
||||
<li>extend the hashtag-to-browser-viewport paradigm beyond 2D documents (XR documents)</li>
|
||||
<li>Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) to show <a href="#visible-links">visible links</a> (and augmenting text with <a href="https://github.com/coderofsalvation/tagbibs">bibs</a> / <a href="https://en.wikipedia.org/wiki/BibTeX">BibTags</a> appendices (see <a href="https://visual-meta.info">visual-meta</a> e.g.)</li>
|
||||
<li>unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
|
@ -139,28 +139,60 @@ Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a
|
|||
<td>positions camera, triggers scene-preset/time</td>
|
||||
<td>jumps/scrolls to chapter</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>src metadata</td>
|
||||
<td>renders content and offers sourceportation</td>
|
||||
<td>renders content</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>teleports to other position or XR document</td>
|
||||
<td>jumps to other chapter or HTML document</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>draws visible connection(s) for XRWG ‘tag’</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>triggers predefined view</td>
|
||||
<td>Media fragments</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>repositions camera or animation-range</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<blockquote>
|
||||
<p>XR Fragments does not look at XR (or the web) thru the lens of HTML.<br>But approaches things from a higherlevel browser-perspective:</p>
|
||||
<p>XR Fragments does not look at XR (or the web) thru the lens of HTML.<br>But approaches things from a higherlevel browser- and feedbackloop perspective:</p>
|
||||
</blockquote>
|
||||
|
||||
<pre><code> +----------------------------------------------------------------------------------------------+
|
||||
| |
|
||||
| the soul of any URL: ://macro /meso ?micro #nano |
|
||||
| |
|
||||
| 2D URL: ://library.com /document ?search #chapter |
|
||||
| |
|
||||
| 4D URL: ://park.com /4Dscene.fbx --> ?search --> #view ---> hashbus |
|
||||
| │ | |
|
||||
| XRWG <---------------------<------------+ |
|
||||
| │ | |
|
||||
| ├─ objects --------------->------------| |
|
||||
| └─ text --------------->------------+ |
|
||||
| |
|
||||
| |
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
<pre><code> +──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ the soul of any URL: ://macro /meso ?micro #nano │
|
||||
│ │
|
||||
│ 2D URL: ://library.com /document ?search #chapter │
|
||||
│ │
|
||||
│ 4D URL: ://park.com /4Dscene.fbx ──> ?misc ──> #view ───> hashbus │
|
||||
│ │ #query │ │
|
||||
│ │ #tag │ │
|
||||
│ │ │ │
|
||||
│ XRWG <─────────────────────<────────────+ │
|
||||
│ │ │ │
|
||||
│ ├─ objects ───────────────>────────────│ │
|
||||
│ └─ text ───────────────>────────────+ │
|
||||
│ │
|
||||
│ │
|
||||
+──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
|
||||
</code></pre>
|
||||
|
||||
|
@ -269,7 +301,8 @@ sub-delims = "," / "="
|
|||
<th>key</th>
|
||||
<th>type</th>
|
||||
<th>example (JSON)</th>
|
||||
<th>info</th>
|
||||
<th>function</th>
|
||||
<th>existing compatibility</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
@ -278,208 +311,149 @@ sub-delims = "," / "="
|
|||
<td><code>name</code></td>
|
||||
<td>string</td>
|
||||
<td><code>"name": "cube"</code></td>
|
||||
<td>available in all 3D fileformats & scenes</td>
|
||||
<td>identify/tag</td>
|
||||
<td>object supported in all 3D fileformats & scenes</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>tag</code></td>
|
||||
<td>string</td>
|
||||
<td><code>"tag": "cubes geo"</code></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>tag object</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>href</code></td>
|
||||
<td>string</td>
|
||||
<td><code>"href": "b.gltf"</code></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>XR teleport</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>src</code></td>
|
||||
<td>string</td>
|
||||
<td><code>"src": "#cube"</code></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>XR embed / teleport</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Popular compatible 3D fileformats: <code>.gltf</code>, <code>.obj</code>, <code>.fbx</code>, <code>.usdz</code>, <code>.json</code> (THREE.js), <code>.dae</code> and so on.</p>
|
||||
<p>Supported popular compatible 3D fileformats: <code>.gltf</code>, <code>.obj</code>, <code>.fbx</code>, <code>.usdz</code>, <code>.json</code> (THREE.js), <code>.dae</code> and so on.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>NOTE: XR Fragments are file-agnostic, which means that the metadata exist in programmatic 3D scene(nodes) too.</p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="spatial-referencing-3d">Spatial Referencing 3D</h1>
|
||||
|
||||
<pre><code>
|
||||
my.io/scene.fbx
|
||||
+─────────────────────────────+
|
||||
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor)
|
||||
│ +─────────────────────────+ │
|
||||
│ │ building │ │ src: http://my.io/scene.fbx#building (includes mainobject,floor)
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ │ │ mainobject │ │ │ src: http://my.io/scene.fbx#mainobject (includes floor)
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ │ │ floor │ │ │ │ src: http://my.io/scene.fbx#floor (just floor object)
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ +─────────────────────────+ │
|
||||
+─────────────────────────────+
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>Clever nested design of 3D scenes allow great ways for re-using content, and/or previewing scenes.<br>
|
||||
For example, to render a portal with a preview-version of the scene, create an 3D object with:</p>
|
||||
|
||||
<ul>
|
||||
<li>href: <code>https://scene.fbx</code></li>
|
||||
<li>src: <code>https://otherworld.gltf#mainobject</code></li>
|
||||
</ul>
|
||||
|
||||
<blockquote>
|
||||
<p>It also allows <strong>sourceportation</strong>, which basically means the enduser can teleport to the original XR Document of an <code>src</code> embedded object, and see a visible connection to the particular embedded object.</p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="navigating-3d">Navigating 3D</h1>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>fragment</th>
|
||||
<th>type</th>
|
||||
<th>functionality</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>#pos</b>=0,0,0</td>
|
||||
<td>vector3</td>
|
||||
<td>(re)position camera</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b>#t</b>=0,100</td>
|
||||
<td>vector2</td>
|
||||
<td>(re)position looprange of scene-animation or <code>src</code>-mediacontent</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b>#rot</b>=0,90,0</td>
|
||||
<td>vector3</td>
|
||||
<td>rotate camera</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js">» example implementation</a><br>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/issues/5">» discussion</a><br></p>
|
||||
|
||||
<ol>
|
||||
<li>the Y-coordinate of <code>pos</code> identifies the floorposition. This means that desktop-projections usually need to add 1.5m (average person height) on top (which is done automatically by VR/AR headsets).</li>
|
||||
<li>set the position of the camera accordingly to the vector3 values of <code>#pos</code></li>
|
||||
<li><code>rot</code> sets the rotation of the camera (only for non-VR/AR headsets)</li>
|
||||
<li><code>t</code> sets the animation-range of the current scene animation(s) or <code>src</code>-mediacontent (video/audioframes e.g., use <code>t=7,7</code> to ‘STOP’ at certain frame)</li>
|
||||
<li>in case an <code>href</code> does not mention any <code>pos</code>-coordinate, <code>pos=0,0,0</code> will be assumed</li>
|
||||
</ol>
|
||||
|
||||
<p>Here’s an ascii representation of a 3D scene-graph which contains 3D objects <code>◻</code> and their metadata:</p>
|
||||
|
||||
<pre><code> +--------------------------------------------------------+
|
||||
| |
|
||||
| index.gltf |
|
||||
| │ |
|
||||
| ├── ◻ buttonA |
|
||||
| │ └ href: #pos=1,0,1&t=100,200 |
|
||||
| │ |
|
||||
| └── ◻ buttonB |
|
||||
| └ href: other.fbx | <-- file-agnostic (can be .gltf .obj etc)
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
<pre><code> +────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ index.gltf │
|
||||
│ │ │
|
||||
│ ├── ◻ buttonA │
|
||||
│ │ └ href: #pos=1,0,1&t=100,200 │
|
||||
│ │ │
|
||||
│ └── ◻ buttonB │
|
||||
│ └ href: other.fbx │ <── file─agnostic (can be .gltf .obj etc)
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>An XR Fragment-compatible browser viewing this scene, allows the end-user to interact with the <code>buttonA</code> and <code>buttonB</code>.<br>
|
||||
In case of <code>buttonA</code> the end-user will be teleported to another location and time in the <strong>current loaded scene</strong>, but <code>buttonB</code> will
|
||||
<strong>replace the current scene</strong> with a new one, like <code>other.fbx</code>.</p>
|
||||
In case of <code>buttonA</code> the end-user will be teleported to another location and time in the <strong>current loaded scene</strong>, but <code>buttonB</code> will <strong>replace the current scene</strong> with a new one, like <code>other.fbx</code>, and assume <code>pos=0,0,0</code>.</p>
|
||||
|
||||
<h1 id="embedding-3d-content">Embedding 3D content</h1>
|
||||
|
||||
<p>Here’s an ascii representation of a 3D scene-graph with 3D objects <code>◻</code> which embeds remote & local 3D objects <code>◻</code> with/out using queries:</p>
|
||||
|
||||
<pre><code> +--------------------------------------------------------+ +-------------------------+
|
||||
| | | |
|
||||
| index.gltf | | ocean.com/aquarium.fbx |
|
||||
| │ | | │ |
|
||||
| ├── ◻ canvas | | └── ◻ fishbowl |
|
||||
| │ └ src: painting.png | | ├─ ◻ bass |
|
||||
| │ | | └─ ◻ tuna |
|
||||
| ├── ◻ aquariumcube | | |
|
||||
| │ └ src: ://rescue.com/fish.gltf#bass%20tuna | +-------------------------+
|
||||
| │ |
|
||||
| ├── ◻ bedroom |
|
||||
| │ └ src: #canvas |
|
||||
| │ |
|
||||
| └── ◻ livingroom |
|
||||
| └ src: #canvas |
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
</code></pre>
|
||||
|
||||
<p>An XR Fragment-compatible browser viewing this scene, lazy-loads and projects <code>painting.png</code> onto the (plane) object called <code>canvas</code> (which is copy-instanced in the bed and livingroom).<br>
|
||||
Also, after lazy-loading <code>ocean.com/aquarium.gltf</code>, only the queried objects <code>bass</code> and <code>tuna</code> will be instanced inside <code>aquariumcube</code>.<br>
|
||||
Resizing will be happen accordingly to its placeholder object <code>aquariumcube</code>, see chapter Scaling.<br></p>
|
||||
<h1 id="top-level-url-processing">Top-level URL processing</h1>
|
||||
|
||||
<blockquote>
|
||||
<p>Instead of cherrypicking objects with <code>#bass&tuna</code> thru <code>src</code>, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.</p>
|
||||
<p>Example URL: <code>://foo/world.gltf#cube&pos=0,0,0</code></p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="xr-fragment-queries">XR Fragment queries</h1>
|
||||
<p>The URL-processing-flow for hypermedia browsers goes like this:</p>
|
||||
|
||||
<p>Include, exclude, hide/shows objects using space-separated strings:</p>
|
||||
<p>1.IF a <code>#cube</code> matches a custom property-key (of an object) in the 3D file/scene (<code>#cube</code>: <code>#......</code>) <b>THEN</b> execute that predefined_view.
|
||||
2.IF scene operators (<code>pos</code>) and/or animation operator (<code>t</code>) are present in the URL then (re)position the camera and/or animation-range accordingly.
|
||||
3.IF no camera-position has been set in <b>step 1 or 2</b> update the top-level URL with <code>#pos=0,0,0</code> (<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/navigator.js#L31]]">example</a>)
|
||||
4.IF a <code>#cube</code> matches the name (of an object) in the 3D file/scene then draw a line from the enduser(’s heart) to that object (to highlight it).
|
||||
5.IF a <code>#cube</code> matches anything else in the XR Word Graph (XRWG) draw wires to them (text or related objects).</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>example</th>
|
||||
<th>outcome</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#q=-sky</code></td>
|
||||
<td>show everything except object named <code>sky</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>#q=-.language .english</code></td>
|
||||
<td>hide everything with tag <code>language</code>, but show all tag <code>english</code> objects</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>#q=price:>2 price:<5</code></td>
|
||||
<td>of all objects with property <code>price</code>, show only objects with value between 2 and 5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>It’s simple but powerful syntax which allows <b>css</b>-like tag/id-selectors with a searchengine prompt-style feeling:</p>
|
||||
|
||||
<ol>
|
||||
<li>queries are a way to traverse a scene, and filter objects based on their tag- or property-values.</li>
|
||||
<li>words starting with <code>.</code> like <code>.german</code> match tag-metadata of 3D objects like <code>"tag":"german"</code></li>
|
||||
<li>words starting with <code>.</code> like <code>.german</code> match tag-metadata of (BibTeX) tags in XR Text objects like <code>@german{KarlHeinz, ...</code> e.g.</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
<p><strong>For example</strong>: <code>#q=.foo</code> is a shorthand for <code>#q=tag:foo</code>, which will select objects with custom property <code>tag</code>:<code>foo</code>. Just a simple <code>#q=cube</code> will simply select an object named <code>cube</code>.</p>
|
||||
</blockquote>
|
||||
|
||||
<ul>
|
||||
<li>see <a href="https://coderofsalvation.github.io/xrfragment.media/queries.mp4">an example video here</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="including-excluding">including/excluding</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>operator</th>
|
||||
<th>info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>-</code></td>
|
||||
<td>removes/hides object(s)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>:</code></td>
|
||||
<td>indicates an object-embedded custom property key/value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>.</code></td>
|
||||
<td>alias for <code>"tag" :".foo"</code> equals <code>tag:foo</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>></code> <code><</code></td>
|
||||
<td>compare float or int number</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>/</code></td>
|
||||
<td>reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by <code>src</code>) (*)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<blockquote>
|
||||
<p>* = <code>#q=-/cube</code> hides object <code>cube</code> only in the root-scene (not nested <code>cube</code> objects)<br> <code>#q=-cube</code> hides both object <code>cube</code> in the root-scene <b>AND</b> nested <code>skybox</code> objects |</p>
|
||||
</blockquote>
|
||||
|
||||
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js">» example implementation</a>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192">» example 3D asset</a>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/issues/3">» discussion</a></p>
|
||||
|
||||
<h2 id="query-parser">Query Parser</h2>
|
||||
|
||||
<p>Here’s how to write a query parser:</p>
|
||||
|
||||
<ol>
|
||||
<li>create an associative array/object to store query-arguments as objects</li>
|
||||
<li>detect object id’s & properties <code>foo:1</code> and <code>foo</code> (reference regex: <code>/^.*:[><=!]?/</code> )</li>
|
||||
<li>detect excluders like <code>-foo</code>,<code>-foo:1</code>,<code>-.foo</code>,<code>-/foo</code> (reference regex: <code>/^-/</code> )</li>
|
||||
<li>detect root selectors like <code>/foo</code> (reference regex: <code>/^[-]?\//</code> )</li>
|
||||
<li>detect tag selectors like <code>.foo</code> (reference regex: <code>/^[-]?tag$/</code> )</li>
|
||||
<li>detect number values like <code>foo:1</code> (reference regex: <code>/^[0-9\.]+$/</code> )</li>
|
||||
<li>expand aliases like <code>.foo</code> into <code>tag:foo</code></li>
|
||||
<li>for every query token split string on <code>:</code></li>
|
||||
<li>create an empty array <code>rules</code></li>
|
||||
<li>then strip key-operator: convert “-foo” into “foo”</li>
|
||||
<li>add operator and value to rule-array</li>
|
||||
<li>therefore we we set <code>id</code> to <code>true</code> or <code>false</code> (false=excluder <code>-</code>)</li>
|
||||
<li>and we set <code>root</code> to <code>true</code> or <code>false</code> (true=<code>/</code> root selector is present)</li>
|
||||
<li>we convert key ‘/foo’ into ‘foo’</li>
|
||||
<li>finally we add the key/value to the store like <code>store.foo = {id:false,root:true}</code> e.g.</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
<p>An example query-parser (which compiles to many languages) can be <a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx">found here</a></p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="embedding-content-src-instancing">Embedding content (src-instancing)</h1>
|
||||
<h1 id="embedding-xr-content-src-instancing">Embedding XR content (src-instancing)</h1>
|
||||
|
||||
<p><code>src</code> is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br>
|
||||
It instances content (in objects) in the current scene/asset.</p>
|
||||
|
@ -496,11 +470,41 @@ It instances content (in objects) in the current scene/asset.</p>
|
|||
<tbody>
|
||||
<tr>
|
||||
<td><code>src</code></td>
|
||||
<td>string (uri or [[predefined view</td>
|
||||
<td>predefined_view]] or [[query</td>
|
||||
<td>string (uri, hashtag/query)</td>
|
||||
<td><code>#cube</code><br><code>#sometag</code><br>#q=-ball_inside_cube<code><br></code>#q=-/sky -rain<code><br></code>#q=-.language .english<code><br></code>#q=price:>2 price:<5`<br><code>https://linux.org/penguin.png</code><br><code>https://linux.world/distrowatch.gltf#t=1,100</code><br><code>linuxapp://conference/nixworkshop/apply.gltf#q=flyer</code><br><code>androidapp://page1?tutorial#pos=0,0,1&t1,100</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Here’s an ascii representation of a 3D scene-graph with 3D objects <code>◻</code> which embeds remote & local 3D objects <code>◻</code> with/out using queries:</p>
|
||||
|
||||
<pre><code> +────────────────────────────────────────────────────────+ +─────────────────────────+
|
||||
│ │ │ │
|
||||
│ index.gltf │ │ ocean.com/aquarium.fbx │
|
||||
│ │ │ │ │ │
|
||||
│ ├── ◻ canvas │ │ └── ◻ fishbowl │
|
||||
│ │ └ src: painting.png │ │ ├─ ◻ bass │
|
||||
│ │ │ │ └─ ◻ tuna │
|
||||
│ ├── ◻ aquariumcube │ │ │
|
||||
│ │ └ src: ://rescue.com/fish.gltf#bass%20tuna │ +─────────────────────────+
|
||||
│ │ │
|
||||
│ ├── ◻ bedroom │
|
||||
│ │ └ src: #canvas │
|
||||
│ │ │
|
||||
│ └── ◻ livingroom │
|
||||
│ └ src: #canvas │
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
</code></pre>
|
||||
|
||||
<p>An XR Fragment-compatible browser viewing this scene, lazy-loads and projects <code>painting.png</code> onto the (plane) object called <code>canvas</code> (which is copy-instanced in the bed and livingroom).<br>
|
||||
Also, after lazy-loading <code>ocean.com/aquarium.gltf</code>, only the queried objects <code>bass</code> and <code>tuna</code> will be instanced inside <code>aquariumcube</code>.<br>
|
||||
Resizing will be happen accordingly to its placeholder object <code>aquariumcube</code>, see chapter Scaling.<br></p>
|
||||
|
||||
<blockquote>
|
||||
<p>Instead of cherrypicking objects with <code>#bass&tuna</code> thru <code>src</code>, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.</p>
|
||||
</blockquote>
|
||||
|
||||
<p><strong>Specification</strong>:</p>
|
||||
|
||||
<ol>
|
||||
<li>local/remote content is instanced by the <code>src</code> (query) value (and attaches it to the placeholder mesh containing the <code>src</code> property)</li>
|
||||
|
@ -509,6 +513,8 @@ It instances content (in objects) in the current scene/asset.</p>
|
|||
<li>the instanced scene (from a <code>src</code> value) should be <b>scaled accordingly</b> to its placeholder object or <b>scaled relatively</b> based on the scale-property (of a geometry-less placeholder, an ‘empty’-object in blender e.g.). For more info see Chapter Scaling.</li>
|
||||
<li><b>external</b> <code>src</code> (file) values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are:</li>
|
||||
<li>when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer.</li>
|
||||
<li>src-values are non-recursive: when linking to an external object (<code>src: foo.fbx#bar</code>), then <code>src</code>-metadata on object <code>bar</code> should be ignored.</li>
|
||||
<li>clicking on external <code>src</code>-values always allow sourceportation: teleporting to the origin URI to which the object belongs.</li>
|
||||
<li>when only one object was cherrypicked (<code>#cube</code> e.g.), set its position to <code>0,0,0</code></li>
|
||||
</ol>
|
||||
|
||||
|
@ -523,7 +529,7 @@ It instances content (in objects) in the current scene/asset.</p>
|
|||
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192">» example 3D asset</a><br>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/issues/4">» discussion</a><br></p>
|
||||
|
||||
<h2 id="referencing-content-href-portals">Referencing content (href portals)</h2>
|
||||
<h1 id="navigating-content-href-portals">Navigating content (href portals)</h1>
|
||||
|
||||
<p>navigation, portals & mutations</p>
|
||||
|
||||
|
@ -565,9 +571,19 @@ It instances content (in objects) in the current scene/asset.</p>
|
|||
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192">» example 3D asset</a><br>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/issues/1">» discussion</a><br></p>
|
||||
|
||||
<h2 id="ux-spec">UX spec</h2>
|
||||
|
||||
<p>End-users should always have read/write access to:</p>
|
||||
|
||||
<ol>
|
||||
<li>the current (toplevel) <b>URL</b> (an URLbar etc)</li>
|
||||
<li>URL-history (a <b>back/forward</b> button e.g.)</li>
|
||||
<li>Clicking/Touching an <code>href</code> navigates (and updates the URL) to another scene/file (and coordinate e.g. in case the URL contains XR Fragments).</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="scaling-instanced-content">Scaling instanced content</h2>
|
||||
|
||||
<p>Sometimes embedded properties (like [[href|href]] or [[src|src]]) instance new objects.<br>
|
||||
<p>Sometimes embedded properties (like <code>src</code>) instance new objects.<br>
|
||||
But what about their scale?<br>
|
||||
How does the scale of the object (with the embedded properties) impact the scale of the referenced content?<br></p>
|
||||
|
||||
|
@ -591,13 +607,131 @@ How does the scale of the object (with the embedded properties) impact the scale
|
|||
</blockquote>
|
||||
|
||||
<ol start="2">
|
||||
<li>ELSE multiply the scale-vector of the instanced scene with the scale-vector of the <b>placeholder</b> object.</li>
|
||||
<li>ELSE multiply the scale-vector of the instanced scene with the scale-vector (a common property of a 3D node) of the <b>placeholder</b> object.</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
<p>TODO: needs intermediate visuals to make things more obvious</p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="xr-fragment-queries">XR Fragment queries</h1>
|
||||
|
||||
<p>Include, exclude, hide/shows objects using space-separated strings:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>example</th>
|
||||
<th>outcome</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#q=-sky</code></td>
|
||||
<td>show everything except object named <code>sky</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>#q=-tag:language tag:english</code></td>
|
||||
<td>hide everything with tag <code>language</code>, but show all tag <code>english</code> objects</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>#q=price:>2 price:<5</code></td>
|
||||
<td>of all objects with property <code>price</code>, show only objects with value between 2 and 5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>It’s simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling:</p>
|
||||
|
||||
<ol>
|
||||
<li>queries are a way to traverse a scene, and filter objects based on their tag- or property-values.</li>
|
||||
<li>words like <code>german</code> match tag-metadata of 3D objects like <code>"tag":"german"</code></li>
|
||||
<li>words like <code>german</code> match (XR Text) objects with (Bib(s)TeX) tags like <code>#KarlHeinz@german</code> or <code>@german{KarlHeinz, ...</code> e.g.</li>
|
||||
</ol>
|
||||
|
||||
<ul>
|
||||
<li>see <a href="https://coderofsalvation.github.io/xrfragment.media/queries.mp4">an (outdated) example video here</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="including-excluding">including/excluding</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>operator</th>
|
||||
<th>info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>-</code></td>
|
||||
<td>removes/hides object(s)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>:</code></td>
|
||||
<td>indicates an object-embedded custom property key/value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>></code> <code><</code></td>
|
||||
<td>compare float or int number</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>/</code></td>
|
||||
<td>reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by <code>src</code>) (*)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<blockquote>
|
||||
<p>* = <code>#q=-/cube</code> hides object <code>cube</code> only in the root-scene (not nested <code>cube</code> objects)<br> <code>#q=-cube</code> hides both object <code>cube</code> in the root-scene <b>AND</b> nested <code>skybox</code> objects |</p>
|
||||
</blockquote>
|
||||
|
||||
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js">» example implementation</a>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192">» example 3D asset</a>
|
||||
<a href="https://github.com/coderofsalvation/xrfragment/issues/3">» discussion</a></p>
|
||||
|
||||
<h2 id="query-parser">Query Parser</h2>
|
||||
|
||||
<p>Here’s how to write a query parser:</p>
|
||||
|
||||
<ol>
|
||||
<li>create an associative array/object to store query-arguments as objects</li>
|
||||
<li>detect object id’s & properties <code>foo:1</code> and <code>foo</code> (reference regex: <code>/^.*:[><=!]?/</code> )</li>
|
||||
<li>detect excluders like <code>-foo</code>,<code>-foo:1</code>,<code>-.foo</code>,<code>-/foo</code> (reference regex: <code>/^-/</code> )</li>
|
||||
<li>detect root selectors like <code>/foo</code> (reference regex: <code>/^[-]?\//</code> )</li>
|
||||
<li>detect number values like <code>foo:1</code> (reference regex: <code>/^[0-9\.]+$/</code> )</li>
|
||||
<li>for every query token split string on <code>:</code></li>
|
||||
<li>create an empty array <code>rules</code></li>
|
||||
<li>then strip key-operator: convert “-foo” into “foo”</li>
|
||||
<li>add operator and value to rule-array</li>
|
||||
<li>therefore we we set <code>id</code> to <code>true</code> or <code>false</code> (false=excluder <code>-</code>)</li>
|
||||
<li>and we set <code>root</code> to <code>true</code> or <code>false</code> (true=<code>/</code> root selector is present)</li>
|
||||
<li>we convert key ‘/foo’ into ‘foo’</li>
|
||||
<li>finally we add the key/value to the store like <code>store.foo = {id:false,root:true}</code> e.g.</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
<p>An example query-parser (which compiles to many languages) can be <a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx">found here</a></p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="visible-links">Visible links</h1>
|
||||
|
||||
<p>When predefined views, XRWG fragments and ID fragments (<code>#cube</code> or <code>#mytag</code> e.g.) are triggered by the enduser (via <code>href</code> of toplevel URL):</p>
|
||||
|
||||
<ol>
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that ID (objectname)</li>
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that <code>tag</code> value</li>
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) containing that in their <code>src</code> or <code>href</code> value</li>
|
||||
</ol>
|
||||
|
||||
<p>The obvious approach is to consult the XRWG, which basically has all these things already collected/organized for you.</p>
|
||||
|
||||
<h1 id="text-in-xr-tagging-linking-to-spatial-objects">Text in XR (tagging,linking to spatial objects)</h1>
|
||||
|
||||
<p>How does XR Fragments interlink text with objects?</p>
|
||||
|
@ -606,9 +740,9 @@ How does the scale of the object (with the embedded properties) impact the scale
|
|||
<p>The XR Fragments does this by collapsing space into a <strong>Word Graph</strong> (the <strong>XRWG</strong>), augmented by Bib(s)Tex.</p>
|
||||
</blockquote>
|
||||
|
||||
<p>Instead of just throwing together all kinds media types into one experience (games), what about the intrinsic connections between them?<br>
|
||||
Why is HTML adopted less in games outside the browser?
|
||||
Through the lens of game-making, ideally metadata must come <strong>with</strong> that text, but not <strong>obfuscate</strong> the text, or <strong>spawning another request</strong> to fetch it.<br>
|
||||
<p>Instead of just throwing together all kinds media types into one experience (games), what about their tagged/semantical relationships?<br>
|
||||
Perhaps the following question is related: why is HTML adopted less in games outside the browser?
|
||||
Through the lens of constructive lazy game-developers, ideally metadata must come <strong>with</strong> text, but not <strong>obfuscate</strong> the text, or <strong>spawning another request</strong> to fetch it.<br>
|
||||
XR Fragments does this by detecting Bib(s)Tex, without introducing a new language or fileformat<br></p>
|
||||
|
||||
<blockquote>
|
||||
|
@ -627,11 +761,12 @@ XR Fragments does this by detecting Bib(s)Tex, without introducing a new languag
|
|||
<li>HTML/RDF/JSON is still great, but is beyond the XRWG-scope (they fit better in the application-layer)</li>
|
||||
<li>Applications don’t have to be able to access the XRWG programmatically, as they can easily generate one themselves by traversing the scene-nodes.</li>
|
||||
<li>The XR Fragment focuses on fast and easy-to-generate end-user controllable word graphs (instead of complex implementations that try to defeat word ambiguity)</li>
|
||||
<li>Tags are the scope for now (supporting <a href="https://github.com/WICG/scroll-to-text-fragment">https://github.com/WICG/scroll-to-text-fragment</a> will be considered)</li>
|
||||
</ol>
|
||||
|
||||
<p>Example:</p>
|
||||
|
||||
<pre><code> http://y.io/z.fbx | Derived XRWG (shown as BibTex)
|
||||
<pre><code> http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
| @house{castle,
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | url = {https://y.io/z.fbx#castle}
|
||||
|
@ -657,7 +792,7 @@ XR Fragments does this by detecting Bib(s)Tex, without introducing a new languag
|
|||
|
||||
<p>Another example:</p>
|
||||
|
||||
<pre><code> http://y.io/z.fbx | Derived XRWG (printed as BibTex)
|
||||
<pre><code> http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
|
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | @house{castle,
|
||||
|
@ -694,18 +829,18 @@ This allows hasslefree authoring and copy-paste of associations <strong>for and
|
|||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>https://my.com/foo.gltf#.baroque</code></td>
|
||||
<td>highlights mesh <code>john</code>, 3D mesh <code>castle</code>, text <code>John built(..)</code></td>
|
||||
<td><code>https://my.com/foo.gltf#baroque</code></td>
|
||||
<td>draws lines between mesh <code>john</code>, 3D mesh <code>castle</code>, text <code>John built(..)</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>https://my.com/foo.gltf#john</code></td>
|
||||
<td>highlights mesh <code>john</code>, and the text <code>John built (..)</code></td>
|
||||
<td>draws lines between mesh <code>john</code>, and the text <code>John built (..)</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>https://my.com/foo.gltf#house</code></td>
|
||||
<td>highlights mesh <code>castle</code>, and other objects with tag <code>house</code> or <code>todo</code></td>
|
||||
<td>draws lines between mesh <code>castle</code>, and other objects with tag <code>house</code> or <code>todo</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -723,7 +858,7 @@ This allows hasslefree authoring and copy-paste of associations <strong>for and
|
|||
<li>wordmatch object-tagnames</li>
|
||||
</ul>
|
||||
|
||||
<p>Spatial wires can be rendered, words/objects can be highlighted/scaled etc.<br>
|
||||
<p>Spatial wires can be rendered between words/objects etc.<br>
|
||||
Some pointers for good UX (but not necessary to be XR Fragment compatible):</p>
|
||||
|
||||
<ol start="9">
|
||||
|
@ -947,6 +1082,16 @@ here are some hashtagbibs followed by bibtex:
|
|||
<li>filter out sensitive data when copy/pasting (XR text with <code>tag:secret</code> e.g.)</li>
|
||||
</ul>
|
||||
|
||||
<h1 id="faq">FAQ</h1>
|
||||
|
||||
<p><strong>Q:</strong> Why is everything HTTP GET-based, what about POST/PUT/DELETE HATEOS<br>
|
||||
<strong>A:</strong> Because it’s out of scope: XR Fragment specifies a read-only way to surf XR documents. These things belong in the application layer (for example, an XR Hypermedia browser can decide to support POST/PUT/DELETE requests for embedded HTML thru <code>src</code> values)</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><strong>Q:</strong> Why isn’t there support for scripting
|
||||
<strong>A:</strong> This is out of scope, and up to the XR hypermedia browser. Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents). In order to prevent this backward-movement (hypermedia tends to liberate people from finnicky scripting) XR Fragments should never unhyperify itself by hardcoupling to a particular markup or scripting language. <a href="https://xrfragment.org/doc/RFC_XR_Macros.html">XR Macro’s</a> are an example of something which is probably smarter and safer for hypermedia browsers to implement, instead of going full-in with a turing-complete scripting language (and suffer the security consequences later).</p>
|
||||
|
||||
<h1 id="iana-considerations">IANA Considerations</h1>
|
||||
|
||||
<p>This document has no IANA actions.</p>
|
||||
|
@ -1010,6 +1155,16 @@ here are some hashtagbibs followed by bibtex:
|
|||
<td>positions camera, triggers scene-preset/time</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>teleportation</td>
|
||||
<td>repositioning the enduser to a different position (or 3D scene/file)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>sourceportation</td>
|
||||
<td>teleporting the enduser to the original XR Document of an <code>src</code> embedded object.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>placeholder object</td>
|
||||
<td>a 3D object which with src-metadata (which will be replaced by the src-data.)</td>
|
||||
|
|
|
@ -106,11 +106,11 @@ XR Fragments allows us to enrich existing dataformats, by recursive use of exist
|
|||
How can we add more features to existing text & 3D scenes, without introducing new dataformats?<br>
|
||||
Historically, there's many attempts to create the ultimate markuplanguage or 3D fileformat.<br>
|
||||
Their lowest common denominator is: (co)authoring using plain text.<br>
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by recursive use of existing technologies:<br>
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:<br>
|
||||
|
||||
1. addressibility and navigation of 3D scenes/objects: [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment) + src/href spatial metadata
|
||||
1. Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) (and augmenting text with [bibs](https://github.com/coderofsalvation/tagbibs) / [BibTags](https://en.wikipedia.org/wiki/BibTeX) appendices (see [visual-meta](https://visual-meta.info) e.g.)
|
||||
1. extend the hashtag-to-browser-viewport paradigm beyond 2D documents (XR documents)
|
||||
1. Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) to show [visible links](#visible-links) (and augmenting text with [bibs](https://github.com/coderofsalvation/tagbibs) / [BibTags](https://en.wikipedia.org/wiki/BibTeX) appendices (see [visual-meta](https://visual-meta.info) e.g.)
|
||||
1. unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents
|
||||
|
||||
> NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible
|
||||
|
||||
|
@ -121,30 +121,37 @@ This also means that the repair-ability of machine-matters should be human frien
|
|||
XR Fragments tries to seek to connect the world of text (semantical web / RDF), and the world of pixels.<br>
|
||||
Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a more integrated path **towards** them, by describing how to make browsers **4D URL-ready**:
|
||||
|
||||
| principle | XR 4D URL | HTML 2D URL |
|
||||
|----------------------|----------------------------------------------|---------------------------------------|
|
||||
| the XRWG | wordgraph (collapses 3D scene to tags) | Ctrl-F (find) |
|
||||
| the hashbus | hashtags map to camera/scene-projections | hashtags map to document positions |
|
||||
| spacetime hashtags | positions camera, triggers scene-preset/time | jumps/scrolls to chapter |
|
||||
| principle | XR 4D URL | HTML 2D URL |
|
||||
|----------------------|-------------------------------------------------|---------------------------------------|
|
||||
| the XRWG | wordgraph (collapses 3D scene to tags) | Ctrl-F (find) |
|
||||
| the hashbus | hashtags map to camera/scene-projections | hashtags map to document positions |
|
||||
| spacetime hashtags | positions camera, triggers scene-preset/time | jumps/scrolls to chapter |
|
||||
| src metadata | renders content and offers sourceportation | renders content |
|
||||
| href metadata | teleports to other position or XR document | jumps to other chapter or HTML document|
|
||||
| href metadata | draws visible connection(s) for XRWG 'tag' | |
|
||||
| href metadata | triggers predefined view | Media fragments |
|
||||
| href metadata | repositions camera or animation-range | |
|
||||
|
||||
> XR Fragments does not look at XR (or the web) thru the lens of HTML.<br>But approaches things from a higherlevel browser-perspective:
|
||||
> XR Fragments does not look at XR (or the web) thru the lens of HTML.<br>But approaches things from a higherlevel browser- and feedbackloop perspective:
|
||||
|
||||
```
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
| |
|
||||
| the soul of any URL: ://macro /meso ?micro #nano |
|
||||
| |
|
||||
| 2D URL: ://library.com /document ?search #chapter |
|
||||
| |
|
||||
| 4D URL: ://park.com /4Dscene.fbx --> ?search --> #view ---> hashbus |
|
||||
| │ | |
|
||||
| XRWG <---------------------<------------+ |
|
||||
| │ | |
|
||||
| ├─ objects --------------->------------| |
|
||||
| └─ text --------------->------------+ |
|
||||
| |
|
||||
| |
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
+──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ the soul of any URL: ://macro /meso ?micro #nano │
|
||||
│ │
|
||||
│ 2D URL: ://library.com /document ?search #chapter │
|
||||
│ │
|
||||
│ 4D URL: ://park.com /4Dscene.fbx ──> ?misc ──> #view ───> hashbus │
|
||||
│ │ #query │ │
|
||||
│ │ #tag │ │
|
||||
│ │ │ │
|
||||
│ XRWG <─────────────────────<────────────+ │
|
||||
│ │ │ │
|
||||
│ ├─ objects ───────────────>────────────│ │
|
||||
│ └─ text ───────────────>────────────+ │
|
||||
│ │
|
||||
│ │
|
||||
+──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
|
||||
```
|
||||
|
||||
|
@ -190,62 +197,125 @@ sub-delims = "," / "="
|
|||
|
||||
# List of metadata for 3D nodes
|
||||
|
||||
| key | type | example (JSON) | info |
|
||||
|--------------|----------|------------------------|--------------------------------------------------------|
|
||||
| `name` | string | `"name": "cube"` | available in all 3D fileformats & scenes |
|
||||
| `tag` | string | `"tag": "cubes geo"` | available through custom property in 3D fileformats |
|
||||
| `href` | string | `"href": "b.gltf"` | available through custom property in 3D fileformats |
|
||||
| `src` | string | `"src": "#cube"` | available through custom property in 3D fileformats |
|
||||
| key | type | example (JSON) | function | existing compatibility |
|
||||
|--------------|----------|------------------------|---------------------|----------------------------------------|
|
||||
| `name` | string | `"name": "cube"` | identify/tag | object supported in all 3D fileformats & scenes |
|
||||
| `tag` | string | `"tag": "cubes geo"` | tag object | custom property in 3D fileformats |
|
||||
| `href` | string | `"href": "b.gltf"` | XR teleport | custom property in 3D fileformats |
|
||||
| `src` | string | `"src": "#cube"` | XR embed / teleport |custom property in 3D fileformats |
|
||||
|
||||
Popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `.json` (THREE.js), `.dae` and so on.
|
||||
Supported popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `.json` (THREE.js), `.dae` and so on.
|
||||
|
||||
> NOTE: XR Fragments are file-agnostic, which means that the metadata exist in programmatic 3D scene(nodes) too.
|
||||
|
||||
# Spatial Referencing 3D
|
||||
|
||||
```
|
||||
|
||||
my.io/scene.fbx
|
||||
+─────────────────────────────+
|
||||
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor)
|
||||
│ +─────────────────────────+ │
|
||||
│ │ building │ │ src: http://my.io/scene.fbx#building (includes mainobject,floor)
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ │ │ mainobject │ │ │ src: http://my.io/scene.fbx#mainobject (includes floor)
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ │ │ floor │ │ │ │ src: http://my.io/scene.fbx#floor (just floor object)
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ +─────────────────────────+ │
|
||||
+─────────────────────────────+
|
||||
|
||||
```
|
||||
|
||||
Clever nested design of 3D scenes allow great ways for re-using content, and/or previewing scenes.<br>
|
||||
For example, to render a portal with a preview-version of the scene, create an 3D object with:
|
||||
|
||||
* href: `https://scene.fbx`
|
||||
* src: `https://otherworld.gltf#mainobject`
|
||||
|
||||
> It also allows **sourceportation**, which basically means the enduser can teleport to the original XR Document of an `src` embedded object, and see a visible connection to the particular embedded object.
|
||||
|
||||
# Navigating 3D
|
||||
|
||||
| fragment | type | functionality |
|
||||
|----------|--------|------------------------------|
|
||||
| <b>#pos</b>=0,0,0 | vector3 | (re)position camera |
|
||||
| <b>#t</b>=0,100 | vector2 | (re)position looprange of scene-animation or `src`-mediacontent |
|
||||
| <b>#rot</b>=0,90,0 | vector3 | rotate camera |
|
||||
|
||||
[» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js)<br>
|
||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/5)<br>
|
||||
|
||||
1. the Y-coordinate of `pos` identifies the floorposition. This means that desktop-projections usually need to add 1.5m (average person height) on top (which is done automatically by VR/AR headsets).
|
||||
1. set the position of the camera accordingly to the vector3 values of `#pos`
|
||||
1. `rot` sets the rotation of the camera (only for non-VR/AR headsets)
|
||||
1. `t` sets the animation-range of the current scene animation(s) or `src`-mediacontent (video/audioframes e.g., use `t=7,7` to 'STOP' at certain frame)
|
||||
1. in case an `href` does not mention any `pos`-coordinate, `pos=0,0,0` will be assumed
|
||||
|
||||
Here's an ascii representation of a 3D scene-graph which contains 3D objects `◻` and their metadata:
|
||||
|
||||
```
|
||||
+--------------------------------------------------------+
|
||||
| |
|
||||
| index.gltf |
|
||||
| │ |
|
||||
| ├── ◻ buttonA |
|
||||
| │ └ href: #pos=1,0,1&t=100,200 |
|
||||
| │ |
|
||||
| └── ◻ buttonB |
|
||||
| └ href: other.fbx | <-- file-agnostic (can be .gltf .obj etc)
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
+────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ index.gltf │
|
||||
│ │ │
|
||||
│ ├── ◻ buttonA │
|
||||
│ │ └ href: #pos=1,0,1&t=100,200 │
|
||||
│ │ │
|
||||
│ └── ◻ buttonB │
|
||||
│ └ href: other.fbx │ <── file─agnostic (can be .gltf .obj etc)
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
|
||||
```
|
||||
|
||||
An XR Fragment-compatible browser viewing this scene, allows the end-user to interact with the `buttonA` and `buttonB`.<br>
|
||||
In case of `buttonA` the end-user will be teleported to another location and time in the **current loaded scene**, but `buttonB` will
|
||||
**replace the current scene** with a new one, like `other.fbx`.
|
||||
In case of `buttonA` the end-user will be teleported to another location and time in the **current loaded scene**, but `buttonB` will **replace the current scene** with a new one, like `other.fbx`, and assume `pos=0,0,0`.
|
||||
|
||||
# Embedding 3D content
|
||||
# Top-level URL processing
|
||||
|
||||
> Example URL: `://foo/world.gltf#cube&pos=0,0,0`
|
||||
|
||||
The URL-processing-flow for hypermedia browsers goes like this:
|
||||
|
||||
1.IF a `#cube` matches a custom property-key (of an object) in the 3D file/scene (`#cube`: `#......`) <b>THEN</b> execute that predefined_view.
|
||||
2.IF scene operators (`pos`) and/or animation operator (`t`) are present in the URL then (re)position the camera and/or animation-range accordingly.
|
||||
3.IF no camera-position has been set in <b>step 1 or 2</b> update the top-level URL with `#pos=0,0,0` ([example](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/navigator.js#L31]]))
|
||||
4.IF a `#cube` matches the name (of an object) in the 3D file/scene then draw a line from the enduser('s heart) to that object (to highlight it).
|
||||
5.IF a `#cube` matches anything else in the XR Word Graph (XRWG) draw wires to them (text or related objects).
|
||||
|
||||
|
||||
# Embedding XR content (src-instancing)
|
||||
|
||||
`src` is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br>
|
||||
It instances content (in objects) in the current scene/asset.
|
||||
|
||||
| fragment | type | example value |
|
||||
|----------|------|---------------|
|
||||
|`src`| string (uri, hashtag/query) | `#cube`<br>`#sometag`<br>#q=-ball_inside_cube`<br>`#q=-/sky -rain`<br>`#q=-.language .english`<br>`#q=price:>2 price:<5`<br>`https://linux.org/penguin.png`<br>`https://linux.world/distrowatch.gltf#t=1,100`<br>`linuxapp://conference/nixworkshop/apply.gltf#q=flyer`<br>`androidapp://page1?tutorial#pos=0,0,1&t1,100`|
|
||||
|
||||
Here's an ascii representation of a 3D scene-graph with 3D objects `◻` which embeds remote & local 3D objects `◻` with/out using queries:
|
||||
|
||||
```
|
||||
+--------------------------------------------------------+ +-------------------------+
|
||||
| | | |
|
||||
| index.gltf | | ocean.com/aquarium.fbx |
|
||||
| │ | | │ |
|
||||
| ├── ◻ canvas | | └── ◻ fishbowl |
|
||||
| │ └ src: painting.png | | ├─ ◻ bass |
|
||||
| │ | | └─ ◻ tuna |
|
||||
| ├── ◻ aquariumcube | | |
|
||||
| │ └ src: ://rescue.com/fish.gltf#bass%20tuna | +-------------------------+
|
||||
| │ |
|
||||
| ├── ◻ bedroom |
|
||||
| │ └ src: #canvas |
|
||||
| │ |
|
||||
| └── ◻ livingroom |
|
||||
| └ src: #canvas |
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
+────────────────────────────────────────────────────────+ +─────────────────────────+
|
||||
│ │ │ │
|
||||
│ index.gltf │ │ ocean.com/aquarium.fbx │
|
||||
│ │ │ │ │ │
|
||||
│ ├── ◻ canvas │ │ └── ◻ fishbowl │
|
||||
│ │ └ src: painting.png │ │ ├─ ◻ bass │
|
||||
│ │ │ │ └─ ◻ tuna │
|
||||
│ ├── ◻ aquariumcube │ │ │
|
||||
│ │ └ src: ://rescue.com/fish.gltf#bass%20tuna │ +─────────────────────────+
|
||||
│ │ │
|
||||
│ ├── ◻ bedroom │
|
||||
│ │ └ src: #canvas │
|
||||
│ │ │
|
||||
│ └── ◻ livingroom │
|
||||
│ └ src: #canvas │
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
```
|
||||
|
||||
An XR Fragment-compatible browser viewing this scene, lazy-loads and projects `painting.png` onto the (plane) object called `canvas` (which is copy-instanced in the bed and livingroom).<br>
|
||||
|
@ -254,72 +324,7 @@ Resizing will be happen accordingly to its placeholder object `aquariumcube`, se
|
|||
|
||||
> Instead of cherrypicking objects with `#bass&tuna` thru `src`, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.
|
||||
|
||||
# XR Fragment queries
|
||||
|
||||
Include, exclude, hide/shows objects using space-separated strings:
|
||||
|
||||
| example | outcome |
|
||||
|----------------------------------|------------------------------------------------------------------------------------|
|
||||
| `#q=-sky` | show everything except object named `sky` |
|
||||
| `#q=-.language .english` | hide everything with tag `language`, but show all tag `english` objects |
|
||||
| `#q=price:>2 price:<5` | of all objects with property `price`, show only objects with value between 2 and 5 |
|
||||
|
||||
It's simple but powerful syntax which allows <b>css</b>-like tag/id-selectors with a searchengine prompt-style feeling:
|
||||
|
||||
1. queries are a way to traverse a scene, and filter objects based on their tag- or property-values.
|
||||
1. words starting with `.` like `.german` match tag-metadata of 3D objects like `"tag":"german"`
|
||||
1. words starting with `.` like `.german` match tag-metadata of (BibTeX) tags in XR Text objects like `@german{KarlHeinz, ...` e.g.
|
||||
|
||||
> **For example**: `#q=.foo` is a shorthand for `#q=tag:foo`, which will select objects with custom property `tag`:`foo`. Just a simple `#q=cube` will simply select an object named `cube`.
|
||||
|
||||
* see [an example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4)
|
||||
|
||||
## including/excluding
|
||||
|
||||
| operator | info |
|
||||
|----------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-` | removes/hides object(s) |
|
||||
| `:` | indicates an object-embedded custom property key/value |
|
||||
| `.` | alias for `"tag" :".foo"` equals `tag:foo` |
|
||||
| `>` `<` | compare float or int number |
|
||||
| `/` | reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) |
|
||||
|
||||
> \* = `#q=-/cube` hides object `cube` only in the root-scene (not nested `cube` objects)<br> `#q=-cube` hides both object `cube` in the root-scene <b>AND</b> nested `skybox` objects |
|
||||
|
||||
[» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js)
|
||||
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192)
|
||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/3)
|
||||
|
||||
## Query Parser
|
||||
|
||||
Here's how to write a query parser:
|
||||
|
||||
1. create an associative array/object to store query-arguments as objects
|
||||
1. detect object id's & properties `foo:1` and `foo` (reference regex: `/^.*:[><=!]?/` )
|
||||
1. detect excluders like `-foo`,`-foo:1`,`-.foo`,`-/foo` (reference regex: `/^-/` )
|
||||
1. detect root selectors like `/foo` (reference regex: `/^[-]?\//` )
|
||||
1. detect tag selectors like `.foo` (reference regex: `/^[-]?tag$/` )
|
||||
1. detect number values like `foo:1` (reference regex: `/^[0-9\.]+$/` )
|
||||
1. expand aliases like `.foo` into `tag:foo`
|
||||
1. for every query token split string on `:`
|
||||
1. create an empty array `rules`
|
||||
1. then strip key-operator: convert "-foo" into "foo"
|
||||
1. add operator and value to rule-array
|
||||
1. therefore we we set `id` to `true` or `false` (false=excluder `-`)
|
||||
1. and we set `root` to `true` or `false` (true=`/` root selector is present)
|
||||
1. we convert key '/foo' into 'foo'
|
||||
1. finally we add the key/value to the store like `store.foo = {id:false,root:true}` e.g.
|
||||
|
||||
> An example query-parser (which compiles to many languages) can be [found here](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx)
|
||||
|
||||
# Embedding content (src-instancing)
|
||||
|
||||
`src` is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br>
|
||||
It instances content (in objects) in the current scene/asset.
|
||||
|
||||
| fragment | type | example value |
|
||||
|----------|------|---------------|
|
||||
|`src`| string (uri or [[predefined view|predefined_view]] or [[query|queries]]) | `#cube`<br>`#q=-ball_inside_cube`<br>`#q=-/sky -rain`<br>`#q=-.language .english`<br>`#q=price:>2 price:<5`<br>`https://linux.org/penguin.png`<br>`https://linux.world/distrowatch.gltf#t=1,100`<br>`linuxapp://conference/nixworkshop/apply.gltf#q=flyer`<br>`androidapp://page1?tutorial#pos=0,0,1&t1,100`|
|
||||
**Specification**:
|
||||
|
||||
1. local/remote content is instanced by the `src` (query) value (and attaches it to the placeholder mesh containing the `src` property)
|
||||
1. <b>local</b> `src` values (URL **starting** with `#`, like `#cube&foo`) means **only** the mentioned objectnames will be copied to the instanced scene (from the current scene) while preserving their names (to support recursive selectors). [(example code)](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js)
|
||||
|
@ -327,6 +332,8 @@ It instances content (in objects) in the current scene/asset.
|
|||
1. the instanced scene (from a `src` value) should be <b>scaled accordingly</b> to its placeholder object or <b>scaled relatively</b> based on the scale-property (of a geometry-less placeholder, an 'empty'-object in blender e.g.). For more info see Chapter Scaling.
|
||||
1. <b>external</b> `src` (file) values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are:
|
||||
1. when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer.
|
||||
1. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored.
|
||||
1. clicking on external `src`-values always allow sourceportation: teleporting to the origin URI to which the object belongs.
|
||||
1. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0`
|
||||
|
||||
* `model/gltf+json`
|
||||
|
@ -338,7 +345,7 @@ It instances content (in objects) in the current scene/asset.
|
|||
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192)<br>
|
||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/4)<br>
|
||||
|
||||
## Referencing content (href portals)
|
||||
# Navigating content (href portals)
|
||||
|
||||
navigation, portals & mutations
|
||||
|
||||
|
@ -364,9 +371,18 @@ navigation, portals & mutations
|
|||
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192)<br>
|
||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/1)<br>
|
||||
|
||||
## UX spec
|
||||
|
||||
End-users should always have read/write access to:
|
||||
|
||||
1. the current (toplevel) <b>URL</b> (an URLbar etc)
|
||||
2. URL-history (a <b>back/forward</b> button e.g.)
|
||||
3. Clicking/Touching an `href` navigates (and updates the URL) to another scene/file (and coordinate e.g. in case the URL contains XR Fragments).
|
||||
|
||||
|
||||
## Scaling instanced content
|
||||
|
||||
Sometimes embedded properties (like [[href|href]] or [[src|src]]) instance new objects.<br>
|
||||
Sometimes embedded properties (like `src`) instance new objects.<br>
|
||||
But what about their scale?<br>
|
||||
How does the scale of the object (with the embedded properties) impact the scale of the referenced content?<br>
|
||||
|
||||
|
@ -381,10 +397,73 @@ How does the scale of the object (with the embedded properties) impact the scale
|
|||
|
||||
> REASON: non-empty placeholder object can act as a protective bounding-box (for remote content of which might grow over time e.g.)
|
||||
|
||||
2. ELSE multiply the scale-vector of the instanced scene with the scale-vector of the <b>placeholder</b> object.
|
||||
2. ELSE multiply the scale-vector of the instanced scene with the scale-vector (a common property of a 3D node) of the <b>placeholder</b> object.
|
||||
|
||||
> TODO: needs intermediate visuals to make things more obvious
|
||||
|
||||
# XR Fragment queries
|
||||
|
||||
Include, exclude, hide/shows objects using space-separated strings:
|
||||
|
||||
| example | outcome |
|
||||
|----------------------------------|------------------------------------------------------------------------------------|
|
||||
| `#q=-sky` | show everything except object named `sky` |
|
||||
| `#q=-tag:language tag:english` | hide everything with tag `language`, but show all tag `english` objects |
|
||||
| `#q=price:>2 price:<5` | of all objects with property `price`, show only objects with value between 2 and 5 |
|
||||
|
||||
It's simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling:
|
||||
|
||||
1. queries are a way to traverse a scene, and filter objects based on their tag- or property-values.
|
||||
1. words like `german` match tag-metadata of 3D objects like `"tag":"german"`
|
||||
1. words like `german` match (XR Text) objects with (Bib(s)TeX) tags like `#KarlHeinz@german` or `@german{KarlHeinz, ...` e.g.
|
||||
|
||||
* see [an (outdated) example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4)
|
||||
|
||||
## including/excluding
|
||||
|
||||
| operator | info |
|
||||
|----------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-` | removes/hides object(s) |
|
||||
| `:` | indicates an object-embedded custom property key/value |
|
||||
| `>` `<` | compare float or int number |
|
||||
| `/` | reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) |
|
||||
|
||||
> \* = `#q=-/cube` hides object `cube` only in the root-scene (not nested `cube` objects)<br> `#q=-cube` hides both object `cube` in the root-scene <b>AND</b> nested `skybox` objects |
|
||||
|
||||
[» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js)
|
||||
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192)
|
||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/3)
|
||||
|
||||
## Query Parser
|
||||
|
||||
Here's how to write a query parser:
|
||||
|
||||
1. create an associative array/object to store query-arguments as objects
|
||||
1. detect object id's & properties `foo:1` and `foo` (reference regex: `/^.*:[><=!]?/` )
|
||||
1. detect excluders like `-foo`,`-foo:1`,`-.foo`,`-/foo` (reference regex: `/^-/` )
|
||||
1. detect root selectors like `/foo` (reference regex: `/^[-]?\//` )
|
||||
1. detect number values like `foo:1` (reference regex: `/^[0-9\.]+$/` )
|
||||
1. for every query token split string on `:`
|
||||
1. create an empty array `rules`
|
||||
1. then strip key-operator: convert "-foo" into "foo"
|
||||
1. add operator and value to rule-array
|
||||
1. therefore we we set `id` to `true` or `false` (false=excluder `-`)
|
||||
1. and we set `root` to `true` or `false` (true=`/` root selector is present)
|
||||
1. we convert key '/foo' into 'foo'
|
||||
1. finally we add the key/value to the store like `store.foo = {id:false,root:true}` e.g.
|
||||
|
||||
> An example query-parser (which compiles to many languages) can be [found here](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx)
|
||||
|
||||
# Visible links
|
||||
|
||||
When predefined views, XRWG fragments and ID fragments (`#cube` or `#mytag` e.g.) are triggered by the enduser (via `href` of toplevel URL):
|
||||
|
||||
1. draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that ID (objectname)
|
||||
1. draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that `tag` value
|
||||
1. draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) containing that in their `src` or `href` value
|
||||
|
||||
The obvious approach is to consult the XRWG, which basically has all these things already collected/organized for you.
|
||||
|
||||
|
||||
# Text in XR (tagging,linking to spatial objects)
|
||||
|
||||
|
@ -392,9 +471,9 @@ How does XR Fragments interlink text with objects?
|
|||
|
||||
> The XR Fragments does this by collapsing space into a **Word Graph** (the **XRWG**), augmented by Bib(s)Tex.
|
||||
|
||||
Instead of just throwing together all kinds media types into one experience (games), what about the intrinsic connections between them?<br>
|
||||
Why is HTML adopted less in games outside the browser?
|
||||
Through the lens of game-making, ideally metadata must come **with** that text, but not **obfuscate** the text, or **spawning another request** to fetch it.<br>
|
||||
Instead of just throwing together all kinds media types into one experience (games), what about their tagged/semantical relationships?<br>
|
||||
Perhaps the following question is related: why is HTML adopted less in games outside the browser?
|
||||
Through the lens of constructive lazy game-developers, ideally metadata must come **with** text, but not **obfuscate** the text, or **spawning another request** to fetch it.<br>
|
||||
XR Fragments does this by detecting Bib(s)Tex, without introducing a new language or fileformat<br>
|
||||
|
||||
> Why Bib(s)Tex? Because its seems to be the lowest common denominator for an human-curated XRWG (extendable by speech/scanner/writing/typing e.g, see [further motivation here](https://github.com/coderofsalvation/hashtagbibs#bibs--bibtex-combo-lowest-common-denominator-for-linking-data))
|
||||
|
@ -410,11 +489,12 @@ Hence:
|
|||
7. HTML/RDF/JSON is still great, but is beyond the XRWG-scope (they fit better in the application-layer)
|
||||
8. Applications don't have to be able to access the XRWG programmatically, as they can easily generate one themselves by traversing the scene-nodes.
|
||||
9. The XR Fragment focuses on fast and easy-to-generate end-user controllable word graphs (instead of complex implementations that try to defeat word ambiguity)
|
||||
10. Tags are the scope for now (supporting https://github.com/WICG/scroll-to-text-fragment will be considered)
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
http://y.io/z.fbx | Derived XRWG (shown as BibTex)
|
||||
http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
| @house{castle,
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | url = {https://y.io/z.fbx#castle}
|
||||
|
@ -440,7 +520,7 @@ Example:
|
|||
Another example:
|
||||
|
||||
```
|
||||
http://y.io/z.fbx | Derived XRWG (printed as BibTex)
|
||||
http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
|
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | @house{castle,
|
||||
|
@ -467,9 +547,9 @@ This allows hasslefree authoring and copy-paste of associations **for and by hum
|
|||
|
||||
| URL example | Result |
|
||||
|---------------------------------------|---------------------------------------------------------------------------|
|
||||
| `https://my.com/foo.gltf#.baroque` | highlights mesh `john`, 3D mesh `castle`, text `John built(..)` |
|
||||
| `https://my.com/foo.gltf#john` | highlights mesh `john`, and the text `John built (..)` |
|
||||
| `https://my.com/foo.gltf#house` | highlights mesh `castle`, and other objects with tag `house` or `todo` |
|
||||
| `https://my.com/foo.gltf#baroque` | draws lines between mesh `john`, 3D mesh `castle`, text `John built(..)` |
|
||||
| `https://my.com/foo.gltf#john` | draws lines between mesh `john`, and the text `John built (..)` |
|
||||
| `https://my.com/foo.gltf#house` | draws lines between mesh `castle`, and other objects with tag `house` or `todo` |
|
||||
|
||||
> [hashtagbibs](https://github.com/coderofsalvation/hashtagbibs) potentially allow the enduser to annotate text/objects by **speaking/typing/scanning associations**, which the XR Browser saves to remotestorage (or localStorage per toplevel URL). As well as, referencing BibTags per URI later on: `https://y.io/z.fbx#@baroque@todo` e.g.
|
||||
|
||||
|
@ -480,7 +560,7 @@ The XRWG allows XR Browsers to show/hide relationships in realtime at various le
|
|||
* wordmatch object-names
|
||||
* wordmatch object-tagnames
|
||||
|
||||
Spatial wires can be rendered, words/objects can be highlighted/scaled etc.<br>
|
||||
Spatial wires can be rendered between words/objects etc.<br>
|
||||
Some pointers for good UX (but not necessary to be XR Fragment compatible):
|
||||
|
||||
9. The XR Browser needs to adjust tag-scope based on the endusers needs/focus (infinite tagging only makes sense when environment is scaled down significantly)
|
||||
|
@ -688,6 +768,16 @@ Since XR Text contains metadata too, the user should be able to set up tagging-r
|
|||
|
||||
* filter out sensitive data when copy/pasting (XR text with `tag:secret` e.g.)
|
||||
|
||||
# FAQ
|
||||
|
||||
**Q:** Why is everything HTTP GET-based, what about POST/PUT/DELETE HATEOS<br>
|
||||
**A:** Because it's out of scope: XR Fragment specifies a read-only way to surf XR documents. These things belong in the application layer (for example, an XR Hypermedia browser can decide to support POST/PUT/DELETE requests for embedded HTML thru `src` values)
|
||||
|
||||
---
|
||||
|
||||
**Q:** Why isn't there support for scripting
|
||||
**A:** This is out of scope, and up to the XR hypermedia browser. Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents). In order to prevent this backward-movement (hypermedia tends to liberate people from finnicky scripting) XR Fragments should never unhyperify itself by hardcoupling to a particular markup or scripting language. [XR Macro's](https://xrfragment.org/doc/RFC_XR_Macros.html) are an example of something which is probably smarter and safer for hypermedia browsers to implement, instead of going full-in with a turing-complete scripting language (and suffer the security consequences later).
|
||||
|
||||
# IANA Considerations
|
||||
|
||||
This document has no IANA actions.
|
||||
|
@ -710,6 +800,8 @@ This document has no IANA actions.
|
|||
|the XRWG | wordgraph (collapses 3D scene to tags) |
|
||||
|the hashbus | hashtags map to camera/scene-projections |
|
||||
|spacetime hashtags | positions camera, triggers scene-preset/time |
|
||||
|teleportation | repositioning the enduser to a different position (or 3D scene/file) |
|
||||
|sourceportation | teleporting the enduser to the original XR Document of an `src` embedded object. |
|
||||
|placeholder object | a 3D object which with src-metadata (which will be replaced by the src-data.) |
|
||||
|src | (HTML-piggybacked) metadata of a 3D object which instances content |
|
||||
|href | (HTML-piggybacked) metadata of a 3D object which links to content |
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,13 +30,13 @@ Historically, there's many attempts to create the ultimate markuplanguage or 3D
|
|||
|
||||
Their lowest common denominator is: (co)authoring using plain text.<br />
|
||||
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by recursive use of existing technologies:<br />
|
||||
XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:<br />
|
||||
</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>addressibility and navigation of 3D scenes/objects: <eref target="https://en.wikipedia.org/wiki/URI_fragment">URI Fragments</eref> + src/href spatial metadata</li>
|
||||
<li>Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) (and augmenting text with <eref target="https://github.com/coderofsalvation/tagbibs">bibs</eref> / <eref target="https://en.wikipedia.org/wiki/BibTeX">BibTags</eref> appendices (see <eref target="https://visual-meta.info">visual-meta</eref> e.g.)</li>
|
||||
<li>extend the hashtag-to-browser-viewport paradigm beyond 2D documents (XR documents)</li>
|
||||
<li>Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) to show <eref target="#visible-links">visible links</eref> (and augmenting text with <eref target="https://github.com/coderofsalvation/tagbibs">bibs</eref> / <eref target="https://en.wikipedia.org/wiki/BibTeX">BibTags</eref> appendices (see <eref target="https://visual-meta.info">visual-meta</eref> e.g.)</li>
|
||||
<li>unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents</li>
|
||||
</ol>
|
||||
<blockquote><t>NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible</t>
|
||||
</blockquote></section>
|
||||
|
@ -76,25 +76,57 @@ Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a
|
|||
<td>positions camera, triggers scene-preset/time</td>
|
||||
<td>jumps/scrolls to chapter</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>src metadata</td>
|
||||
<td>renders content and offers sourceportation</td>
|
||||
<td>renders content</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>teleports to other position or XR document</td>
|
||||
<td>jumps to other chapter or HTML document</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>draws visible connection(s) for XRWG 'tag'</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>triggers predefined view</td>
|
||||
<td>Media fragments</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>href metadata</td>
|
||||
<td>repositions camera or animation-range</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><blockquote><t>XR Fragments does not look at XR (or the web) thru the lens of HTML.<br />
|
||||
But approaches things from a higherlevel browser-perspective:</t>
|
||||
But approaches things from a higherlevel browser- and feedbackloop perspective:</t>
|
||||
</blockquote>
|
||||
<artwork> +----------------------------------------------------------------------------------------------+
|
||||
| |
|
||||
| the soul of any URL: ://macro /meso ?micro #nano |
|
||||
| |
|
||||
| 2D URL: ://library.com /document ?search #chapter |
|
||||
| |
|
||||
| 4D URL: ://park.com /4Dscene.fbx --> ?search --> #view ---> hashbus |
|
||||
| │ | |
|
||||
| XRWG <---------------------<------------+ |
|
||||
| │ | |
|
||||
| ├─ objects --------------->------------| |
|
||||
| └─ text --------------->------------+ |
|
||||
| |
|
||||
| |
|
||||
+----------------------------------------------------------------------------------------------+
|
||||
<artwork> +──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ the soul of any URL: ://macro /meso ?micro #nano │
|
||||
│ │
|
||||
│ 2D URL: ://library.com /document ?search #chapter │
|
||||
│ │
|
||||
│ 4D URL: ://park.com /4Dscene.fbx ──> ?misc ──> #view ───> hashbus │
|
||||
│ │ #query │ │
|
||||
│ │ #tag │ │
|
||||
│ │ │ │
|
||||
│ XRWG <─────────────────────<────────────+ │
|
||||
│ │ │ │
|
||||
│ ├─ objects ───────────────>────────────│ │
|
||||
│ └─ text ───────────────>────────────+ │
|
||||
│ │
|
||||
│ │
|
||||
+──────────────────────────────────────────────────────────────────────────────────────────────+
|
||||
|
||||
</artwork>
|
||||
<t>Traditional webbrowsers can become 4D document-ready by:</t>
|
||||
|
@ -190,7 +222,8 @@ sub-delims = "," / "="
|
|||
<th>key</th>
|
||||
<th>type</th>
|
||||
<th>example (JSON)</th>
|
||||
<th>info</th>
|
||||
<th>function</th>
|
||||
<th>existing compatibility</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
@ -199,193 +232,140 @@ sub-delims = "," / "="
|
|||
<td><tt>name</tt></td>
|
||||
<td>string</td>
|
||||
<td><tt>"name": "cube"</tt></td>
|
||||
<td>available in all 3D fileformats & scenes</td>
|
||||
<td>identify/tag</td>
|
||||
<td>object supported in all 3D fileformats & scenes</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>tag</tt></td>
|
||||
<td>string</td>
|
||||
<td><tt>"tag": "cubes geo"</tt></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>tag object</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>href</tt></td>
|
||||
<td>string</td>
|
||||
<td><tt>"href": "b.gltf"</tt></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>XR teleport</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>src</tt></td>
|
||||
<td>string</td>
|
||||
<td><tt>"src": "#cube"</tt></td>
|
||||
<td>available through custom property in 3D fileformats</td>
|
||||
<td>XR embed / teleport</td>
|
||||
<td>custom property in 3D fileformats</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><t>Popular compatible 3D fileformats: <tt>.gltf</tt>, <tt>.obj</tt>, <tt>.fbx</tt>, <tt>.usdz</tt>, <tt>.json</tt> (THREE.js), <tt>.dae</tt> and so on.</t>
|
||||
</table><t>Supported popular compatible 3D fileformats: <tt>.gltf</tt>, <tt>.obj</tt>, <tt>.fbx</tt>, <tt>.usdz</tt>, <tt>.json</tt> (THREE.js), <tt>.dae</tt> and so on.</t>
|
||||
<blockquote><t>NOTE: XR Fragments are file-agnostic, which means that the metadata exist in programmatic 3D scene(nodes) too.</t>
|
||||
</blockquote></section>
|
||||
|
||||
<section anchor="spatial-referencing-3d"><name>Spatial Referencing 3D</name>
|
||||
|
||||
<artwork>
|
||||
my.io/scene.fbx
|
||||
+─────────────────────────────+
|
||||
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor)
|
||||
│ +─────────────────────────+ │
|
||||
│ │ building │ │ src: http://my.io/scene.fbx#building (includes mainobject,floor)
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ │ │ mainobject │ │ │ src: http://my.io/scene.fbx#mainobject (includes floor)
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ │ │ floor │ │ │ │ src: http://my.io/scene.fbx#floor (just floor object)
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ +─────────────────+ │ │ │
|
||||
│ │ +─────────────────────+ │ │
|
||||
│ +─────────────────────────+ │
|
||||
+─────────────────────────────+
|
||||
|
||||
</artwork>
|
||||
<t>Clever nested design of 3D scenes allow great ways for re-using content, and/or previewing scenes.<br />
|
||||
|
||||
For example, to render a portal with a preview-version of the scene, create an 3D object with:</t>
|
||||
|
||||
<ul spacing="compact">
|
||||
<li>href: <tt>https://scene.fbx</tt></li>
|
||||
<li>src: <tt>https://otherworld.gltf#mainobject</tt></li>
|
||||
</ul>
|
||||
<blockquote><t>It also allows <strong>sourceportation</strong>, which basically means the enduser can teleport to the original XR Document of an <tt>src</tt> embedded object, and see a visible connection to the particular embedded object.</t>
|
||||
</blockquote></section>
|
||||
|
||||
<section anchor="navigating-3d"><name>Navigating 3D</name>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>fragment</th>
|
||||
<th>type</th>
|
||||
<th>functionality</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>#pos</b>=0,0,0</td>
|
||||
<td>vector3</td>
|
||||
<td>(re)position camera</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b>#t</b>=0,100</td>
|
||||
<td>vector2</td>
|
||||
<td>(re)position looprange of scene-animation or <tt>src</tt>-mediacontent</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b>#rot</b>=0,90,0</td>
|
||||
<td>vector3</td>
|
||||
<td>rotate camera</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><t><eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js">» example implementation</eref><br />
|
||||
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/issues/5">» discussion</eref><br />
|
||||
</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>the Y-coordinate of <tt>pos</tt> identifies the floorposition. This means that desktop-projections usually need to add 1.5m (average person height) on top (which is done automatically by VR/AR headsets).</li>
|
||||
<li>set the position of the camera accordingly to the vector3 values of <tt>#pos</tt></li>
|
||||
<li><tt>rot</tt> sets the rotation of the camera (only for non-VR/AR headsets)</li>
|
||||
<li><tt>t</tt> sets the animation-range of the current scene animation(s) or <tt>src</tt>-mediacontent (video/audioframes e.g., use <tt>t=7,7</tt> to 'STOP' at certain frame)</li>
|
||||
<li>in case an <tt>href</tt> does not mention any <tt>pos</tt>-coordinate, <tt>pos=0,0,0</tt> will be assumed</li>
|
||||
</ol>
|
||||
<t>Here's an ascii representation of a 3D scene-graph which contains 3D objects <tt>◻</tt> and their metadata:</t>
|
||||
|
||||
<artwork> +--------------------------------------------------------+
|
||||
| |
|
||||
| index.gltf |
|
||||
| │ |
|
||||
| ├── ◻ buttonA |
|
||||
| │ └ href: #pos=1,0,1&t=100,200 |
|
||||
| │ |
|
||||
| └── ◻ buttonB |
|
||||
| └ href: other.fbx | <-- file-agnostic (can be .gltf .obj etc)
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
<artwork> +────────────────────────────────────────────────────────+
|
||||
│ │
|
||||
│ index.gltf │
|
||||
│ │ │
|
||||
│ ├── ◻ buttonA │
|
||||
│ │ └ href: #pos=1,0,1&t=100,200 │
|
||||
│ │ │
|
||||
│ └── ◻ buttonB │
|
||||
│ └ href: other.fbx │ <── file─agnostic (can be .gltf .obj etc)
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
|
||||
</artwork>
|
||||
<t>An XR Fragment-compatible browser viewing this scene, allows the end-user to interact with the <tt>buttonA</tt> and <tt>buttonB</tt>.<br />
|
||||
|
||||
In case of <tt>buttonA</tt> the end-user will be teleported to another location and time in the <strong>current loaded scene</strong>, but <tt>buttonB</tt> will
|
||||
<strong>replace the current scene</strong> with a new one, like <tt>other.fbx</tt>.</t>
|
||||
In case of <tt>buttonA</tt> the end-user will be teleported to another location and time in the <strong>current loaded scene</strong>, but <tt>buttonB</tt> will <strong>replace the current scene</strong> with a new one, like <tt>other.fbx</tt>, and assume <tt>pos=0,0,0</tt>.</t>
|
||||
</section>
|
||||
|
||||
<section anchor="embedding-3d-content"><name>Embedding 3D content</name>
|
||||
<t>Here's an ascii representation of a 3D scene-graph with 3D objects <tt>◻</tt> which embeds remote & local 3D objects <tt>◻</tt> with/out using queries:</t>
|
||||
|
||||
<artwork> +--------------------------------------------------------+ +-------------------------+
|
||||
| | | |
|
||||
| index.gltf | | ocean.com/aquarium.fbx |
|
||||
| │ | | │ |
|
||||
| ├── ◻ canvas | | └── ◻ fishbowl |
|
||||
| │ └ src: painting.png | | ├─ ◻ bass |
|
||||
| │ | | └─ ◻ tuna |
|
||||
| ├── ◻ aquariumcube | | |
|
||||
| │ └ src: ://rescue.com/fish.gltf#bass%20tuna | +-------------------------+
|
||||
| │ |
|
||||
| ├── ◻ bedroom |
|
||||
| │ └ src: #canvas |
|
||||
| │ |
|
||||
| └── ◻ livingroom |
|
||||
| └ src: #canvas |
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
</artwork>
|
||||
<t>An XR Fragment-compatible browser viewing this scene, lazy-loads and projects <tt>painting.png</tt> onto the (plane) object called <tt>canvas</tt> (which is copy-instanced in the bed and livingroom).<br />
|
||||
|
||||
Also, after lazy-loading <tt>ocean.com/aquarium.gltf</tt>, only the queried objects <tt>bass</tt> and <tt>tuna</tt> will be instanced inside <tt>aquariumcube</tt>.<br />
|
||||
|
||||
Resizing will be happen accordingly to its placeholder object <tt>aquariumcube</tt>, see chapter Scaling.<br />
|
||||
</t>
|
||||
<blockquote><t>Instead of cherrypicking objects with <tt>#bass&tuna</tt> thru <tt>src</tt>, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.</t>
|
||||
</blockquote></section>
|
||||
|
||||
<section anchor="xr-fragment-queries"><name>XR Fragment queries</name>
|
||||
<t>Include, exclude, hide/shows objects using space-separated strings:</t>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>example</th>
|
||||
<th>outcome</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><tt>#q=-sky</tt></td>
|
||||
<td>show everything except object named <tt>sky</tt></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>#q=-.language .english</tt></td>
|
||||
<td>hide everything with tag <tt>language</tt>, but show all tag <tt>english</tt> objects</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>#q=price:>2 price:<5</tt></td>
|
||||
<td>of all objects with property <tt>price</tt>, show only objects with value between 2 and 5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><t>It's simple but powerful syntax which allows <b>css</b>-like tag/id-selectors with a searchengine prompt-style feeling:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>queries are a way to traverse a scene, and filter objects based on their tag- or property-values.</li>
|
||||
<li>words starting with <tt>.</tt> like <tt>.german</tt> match tag-metadata of 3D objects like <tt>"tag":"german"</tt></li>
|
||||
<li>words starting with <tt>.</tt> like <tt>.german</tt> match tag-metadata of (BibTeX) tags in XR Text objects like <tt>@german{KarlHeinz, ...</tt> e.g.</li>
|
||||
</ol>
|
||||
<blockquote><t><strong>For example</strong>: <tt>#q=.foo</tt> is a shorthand for <tt>#q=tag:foo</tt>, which will select objects with custom property <tt>tag</tt>:<tt>foo</tt>. Just a simple <tt>#q=cube</tt> will simply select an object named <tt>cube</tt>.</t>
|
||||
</blockquote>
|
||||
<ul spacing="compact">
|
||||
<li>see <eref target="https://coderofsalvation.github.io/xrfragment.media/queries.mp4">an example video here</eref></li>
|
||||
</ul>
|
||||
|
||||
<section anchor="including-excluding"><name>including/excluding</name>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>operator</th>
|
||||
<th>info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><tt>-</tt></td>
|
||||
<td>removes/hides object(s)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>:</tt></td>
|
||||
<td>indicates an object-embedded custom property key/value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>.</tt></td>
|
||||
<td>alias for <tt>"tag" :".foo"</tt> equals <tt>tag:foo</tt></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>></tt> <tt><</tt></td>
|
||||
<td>compare float or int number</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>/</tt></td>
|
||||
<td>reference to root-scene.<br />
|
||||
Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by <tt>src</tt>) (*)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><blockquote><t>* = <tt>#q=-/cube</tt> hides object <tt>cube</tt> only in the root-scene (not nested <tt>cube</tt> objects)<br />
|
||||
<tt>#q=-cube</tt> hides both object <tt>cube</tt> in the root-scene <b>AND</b> nested <tt>skybox</tt> objects |</t>
|
||||
</blockquote><t><eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js">» example implementation</eref>
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192">» example 3D asset</eref>
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/issues/3">» discussion</eref></t>
|
||||
<section anchor="top-level-url-processing"><name>Top-level URL processing</name>
|
||||
<blockquote><t>Example URL: <tt>://foo/world.gltf#cube&pos=0,0,0</tt></t>
|
||||
</blockquote><t>The URL-processing-flow for hypermedia browsers goes like this:</t>
|
||||
<t>1.IF a <tt>#cube</tt> matches a custom property-key (of an object) in the 3D file/scene (<tt>#cube</tt>: <tt>#......</tt>) <b>THEN</b> execute that predefined_view.
|
||||
2.IF scene operators (<tt>pos</tt>) and/or animation operator (<tt>t</tt>) are present in the URL then (re)position the camera and/or animation-range accordingly.
|
||||
3.IF no camera-position has been set in <b>step 1 or 2</b> update the top-level URL with <tt>#pos=0,0,0</tt> (<eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/navigator.js#L31]]">example</eref>)
|
||||
4.IF a <tt>#cube</tt> matches the name (of an object) in the 3D file/scene then draw a line from the enduser('s heart) to that object (to highlight it).
|
||||
5.IF a <tt>#cube</tt> matches anything else in the XR Word Graph (XRWG) draw wires to them (text or related objects).</t>
|
||||
</section>
|
||||
|
||||
<section anchor="query-parser"><name>Query Parser</name>
|
||||
<t>Here's how to write a query parser:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>create an associative array/object to store query-arguments as objects</li>
|
||||
<li>detect object id's & properties <tt>foo:1</tt> and <tt>foo</tt> (reference regex: <tt>/^.*:[><=!]?/</tt> )</li>
|
||||
<li>detect excluders like <tt>-foo</tt>,<tt>-foo:1</tt>,<tt>-.foo</tt>,<tt>-/foo</tt> (reference regex: <tt>/^-/</tt> )</li>
|
||||
<li>detect root selectors like <tt>/foo</tt> (reference regex: <tt>/^[-]?\//</tt> )</li>
|
||||
<li>detect tag selectors like <tt>.foo</tt> (reference regex: <tt>/^[-]?tag$/</tt> )</li>
|
||||
<li>detect number values like <tt>foo:1</tt> (reference regex: <tt>/^[0-9\.]+$/</tt> )</li>
|
||||
<li>expand aliases like <tt>.foo</tt> into <tt>tag:foo</tt></li>
|
||||
<li>for every query token split string on <tt>:</tt></li>
|
||||
<li>create an empty array <tt>rules</tt></li>
|
||||
<li>then strip key-operator: convert "-foo" into "foo"</li>
|
||||
<li>add operator and value to rule-array</li>
|
||||
<li>therefore we we set <tt>id</tt> to <tt>true</tt> or <tt>false</tt> (false=excluder <tt>-</tt>)</li>
|
||||
<li>and we set <tt>root</tt> to <tt>true</tt> or <tt>false</tt> (true=<tt>/</tt> root selector is present)</li>
|
||||
<li>we convert key '/foo' into 'foo'</li>
|
||||
<li>finally we add the key/value to the store like <tt>store.foo = {id:false,root:true}</tt> e.g.</li>
|
||||
</ol>
|
||||
<blockquote><t>An example query-parser (which compiles to many languages) can be <eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx">found here</eref></t>
|
||||
</blockquote></section>
|
||||
</section>
|
||||
|
||||
<section anchor="embedding-content-src-instancing"><name>Embedding content (src-instancing)</name>
|
||||
<section anchor="embedding-xr-content-src-instancing"><name>Embedding XR content (src-instancing)</name>
|
||||
<t><tt>src</tt> is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br />
|
||||
|
||||
It instances content (in objects) in the current scene/asset.</t>
|
||||
|
@ -401,11 +381,44 @@ It instances content (in objects) in the current scene/asset.</t>
|
|||
<tbody>
|
||||
<tr>
|
||||
<td><tt>src</tt></td>
|
||||
<td>string (uri or [[predefined view</td>
|
||||
<td>predefined_view]] or [[query</td>
|
||||
<td>string (uri, hashtag/query)</td>
|
||||
<td><tt>#cube</tt><br />
|
||||
<tt>#sometag</tt><br />
|
||||
#q=-ball_inside_cube<tt><br></tt>#q=-/sky -rain<tt><br></tt>#q=-.language .english<tt><br></tt>#q=price:>2 price:<5`<br><tt>https://linux.org/penguin.png</tt><br />
|
||||
<tt>https://linux.world/distrowatch.gltf#t=1,100</tt><br />
|
||||
<tt>linuxapp://conference/nixworkshop/apply.gltf#q=flyer</tt><br />
|
||||
<tt>androidapp://page1?tutorial#pos=0,0,1&t1,100</tt></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</table><t>Here's an ascii representation of a 3D scene-graph with 3D objects <tt>◻</tt> which embeds remote & local 3D objects <tt>◻</tt> with/out using queries:</t>
|
||||
|
||||
<artwork> +────────────────────────────────────────────────────────+ +─────────────────────────+
|
||||
│ │ │ │
|
||||
│ index.gltf │ │ ocean.com/aquarium.fbx │
|
||||
│ │ │ │ │ │
|
||||
│ ├── ◻ canvas │ │ └── ◻ fishbowl │
|
||||
│ │ └ src: painting.png │ │ ├─ ◻ bass │
|
||||
│ │ │ │ └─ ◻ tuna │
|
||||
│ ├── ◻ aquariumcube │ │ │
|
||||
│ │ └ src: ://rescue.com/fish.gltf#bass%20tuna │ +─────────────────────────+
|
||||
│ │ │
|
||||
│ ├── ◻ bedroom │
|
||||
│ │ └ src: #canvas │
|
||||
│ │ │
|
||||
│ └── ◻ livingroom │
|
||||
│ └ src: #canvas │
|
||||
│ │
|
||||
+────────────────────────────────────────────────────────+
|
||||
</artwork>
|
||||
<t>An XR Fragment-compatible browser viewing this scene, lazy-loads and projects <tt>painting.png</tt> onto the (plane) object called <tt>canvas</tt> (which is copy-instanced in the bed and livingroom).<br />
|
||||
|
||||
Also, after lazy-loading <tt>ocean.com/aquarium.gltf</tt>, only the queried objects <tt>bass</tt> and <tt>tuna</tt> will be instanced inside <tt>aquariumcube</tt>.<br />
|
||||
|
||||
Resizing will be happen accordingly to its placeholder object <tt>aquariumcube</tt>, see chapter Scaling.<br />
|
||||
</t>
|
||||
<blockquote><t>Instead of cherrypicking objects with <tt>#bass&tuna</tt> thru <tt>src</tt>, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.</t>
|
||||
</blockquote><t><strong>Specification</strong>:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>local/remote content is instanced by the <tt>src</tt> (query) value (and attaches it to the placeholder mesh containing the <tt>src</tt> property)</li>
|
||||
<li><b>local</b> <tt>src</tt> values (URL <strong>starting</strong> with <tt>#</tt>, like <tt>#cube&foo</tt>) means <strong>only</strong> the mentioned objectnames will be copied to the instanced scene (from the current scene) while preserving their names (to support recursive selectors). <eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js">(example code)</eref></li>
|
||||
|
@ -413,6 +426,8 @@ It instances content (in objects) in the current scene/asset.</t>
|
|||
<li>the instanced scene (from a <tt>src</tt> value) should be <b>scaled accordingly</b> to its placeholder object or <b>scaled relatively</b> based on the scale-property (of a geometry-less placeholder, an 'empty'-object in blender e.g.). For more info see Chapter Scaling.</li>
|
||||
<li><b>external</b> <tt>src</tt> (file) values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are:</li>
|
||||
<li>when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer.</li>
|
||||
<li>src-values are non-recursive: when linking to an external object (<tt>src: foo.fbx#bar</tt>), then <tt>src</tt>-metadata on object <tt>bar</tt> should be ignored.</li>
|
||||
<li>clicking on external <tt>src</tt>-values always allow sourceportation: teleporting to the origin URI to which the object belongs.</li>
|
||||
<li>when only one object was cherrypicked (<tt>#cube</tt> e.g.), set its position to <tt>0,0,0</tt></li>
|
||||
</ol>
|
||||
|
||||
|
@ -428,8 +443,9 @@ It instances content (in objects) in the current scene/asset.</t>
|
|||
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/issues/4">» discussion</eref><br />
|
||||
</t>
|
||||
</section>
|
||||
|
||||
<section anchor="referencing-content-href-portals"><name>Referencing content (href portals)</name>
|
||||
<section anchor="navigating-content-href-portals"><name>Navigating content (href portals)</name>
|
||||
<t>navigation, portals & mutations</t>
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -473,10 +489,19 @@ It instances content (in objects) in the current scene/asset.</t>
|
|||
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/issues/1">» discussion</eref><br />
|
||||
</t>
|
||||
|
||||
<section anchor="ux-spec"><name>UX spec</name>
|
||||
<t>End-users should always have read/write access to:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>the current (toplevel) <b>URL</b> (an URLbar etc)</li>
|
||||
<li>URL-history (a <b>back/forward</b> button e.g.)</li>
|
||||
<li>Clicking/Touching an <tt>href</tt> navigates (and updates the URL) to another scene/file (and coordinate e.g. in case the URL contains XR Fragments).</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section anchor="scaling-instanced-content"><name>Scaling instanced content</name>
|
||||
<t>Sometimes embedded properties (like [[href|href]] or [[src|src]]) instance new objects.<br />
|
||||
<t>Sometimes embedded properties (like <tt>src</tt>) instance new objects.<br />
|
||||
|
||||
But what about their scale?<br />
|
||||
|
||||
|
@ -497,19 +522,128 @@ How does the scale of the object (with the embedded properties) impact the scale
|
|||
<blockquote><t>REASON: non-empty placeholder object can act as a protective bounding-box (for remote content of which might grow over time e.g.)</t>
|
||||
</blockquote>
|
||||
<ol spacing="compact" start="2">
|
||||
<li>ELSE multiply the scale-vector of the instanced scene with the scale-vector of the <b>placeholder</b> object.</li>
|
||||
<li>ELSE multiply the scale-vector of the instanced scene with the scale-vector (a common property of a 3D node) of the <b>placeholder</b> object.</li>
|
||||
</ol>
|
||||
<blockquote><t>TODO: needs intermediate visuals to make things more obvious</t>
|
||||
</blockquote></section>
|
||||
</section>
|
||||
|
||||
<section anchor="xr-fragment-queries"><name>XR Fragment queries</name>
|
||||
<t>Include, exclude, hide/shows objects using space-separated strings:</t>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>example</th>
|
||||
<th>outcome</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><tt>#q=-sky</tt></td>
|
||||
<td>show everything except object named <tt>sky</tt></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>#q=-tag:language tag:english</tt></td>
|
||||
<td>hide everything with tag <tt>language</tt>, but show all tag <tt>english</tt> objects</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>#q=price:>2 price:<5</tt></td>
|
||||
<td>of all objects with property <tt>price</tt>, show only objects with value between 2 and 5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><t>It's simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>queries are a way to traverse a scene, and filter objects based on their tag- or property-values.</li>
|
||||
<li>words like <tt>german</tt> match tag-metadata of 3D objects like <tt>"tag":"german"</tt></li>
|
||||
<li>words like <tt>german</tt> match (XR Text) objects with (Bib(s)TeX) tags like <tt>#KarlHeinz@german</tt> or <tt>@german{KarlHeinz, ...</tt> e.g.</li>
|
||||
</ol>
|
||||
|
||||
<ul spacing="compact">
|
||||
<li>see <eref target="https://coderofsalvation.github.io/xrfragment.media/queries.mp4">an (outdated) example video here</eref></li>
|
||||
</ul>
|
||||
|
||||
<section anchor="including-excluding"><name>including/excluding</name>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>operator</th>
|
||||
<th>info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><tt>-</tt></td>
|
||||
<td>removes/hides object(s)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>:</tt></td>
|
||||
<td>indicates an object-embedded custom property key/value</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>></tt> <tt><</tt></td>
|
||||
<td>compare float or int number</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>/</tt></td>
|
||||
<td>reference to root-scene.<br />
|
||||
Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by <tt>src</tt>) (*)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><blockquote><t>* = <tt>#q=-/cube</tt> hides object <tt>cube</tt> only in the root-scene (not nested <tt>cube</tt> objects)<br />
|
||||
<tt>#q=-cube</tt> hides both object <tt>cube</tt> in the root-scene <b>AND</b> nested <tt>skybox</tt> objects |</t>
|
||||
</blockquote><t><eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js">» example implementation</eref>
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192">» example 3D asset</eref>
|
||||
<eref target="https://github.com/coderofsalvation/xrfragment/issues/3">» discussion</eref></t>
|
||||
</section>
|
||||
|
||||
<section anchor="query-parser"><name>Query Parser</name>
|
||||
<t>Here's how to write a query parser:</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>create an associative array/object to store query-arguments as objects</li>
|
||||
<li>detect object id's & properties <tt>foo:1</tt> and <tt>foo</tt> (reference regex: <tt>/^.*:[><=!]?/</tt> )</li>
|
||||
<li>detect excluders like <tt>-foo</tt>,<tt>-foo:1</tt>,<tt>-.foo</tt>,<tt>-/foo</tt> (reference regex: <tt>/^-/</tt> )</li>
|
||||
<li>detect root selectors like <tt>/foo</tt> (reference regex: <tt>/^[-]?\//</tt> )</li>
|
||||
<li>detect number values like <tt>foo:1</tt> (reference regex: <tt>/^[0-9\.]+$/</tt> )</li>
|
||||
<li>for every query token split string on <tt>:</tt></li>
|
||||
<li>create an empty array <tt>rules</tt></li>
|
||||
<li>then strip key-operator: convert "-foo" into "foo"</li>
|
||||
<li>add operator and value to rule-array</li>
|
||||
<li>therefore we we set <tt>id</tt> to <tt>true</tt> or <tt>false</tt> (false=excluder <tt>-</tt>)</li>
|
||||
<li>and we set <tt>root</tt> to <tt>true</tt> or <tt>false</tt> (true=<tt>/</tt> root selector is present)</li>
|
||||
<li>we convert key '/foo' into 'foo'</li>
|
||||
<li>finally we add the key/value to the store like <tt>store.foo = {id:false,root:true}</tt> e.g.</li>
|
||||
</ol>
|
||||
<blockquote><t>An example query-parser (which compiles to many languages) can be <eref target="https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx">found here</eref></t>
|
||||
</blockquote></section>
|
||||
</section>
|
||||
|
||||
<section anchor="visible-links"><name>Visible links</name>
|
||||
<t>When predefined views, XRWG fragments and ID fragments (<tt>#cube</tt> or <tt>#mytag</tt> e.g.) are triggered by the enduser (via <tt>href</tt> of toplevel URL):</t>
|
||||
|
||||
<ol spacing="compact">
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that ID (objectname)</li>
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that <tt>tag</tt> value</li>
|
||||
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) containing that in their <tt>src</tt> or <tt>href</tt> value</li>
|
||||
</ol>
|
||||
<t>The obvious approach is to consult the XRWG, which basically has all these things already collected/organized for you.</t>
|
||||
</section>
|
||||
|
||||
<section anchor="text-in-xr-tagging-linking-to-spatial-objects"><name>Text in XR (tagging,linking to spatial objects)</name>
|
||||
<t>How does XR Fragments interlink text with objects?</t>
|
||||
<blockquote><t>The XR Fragments does this by collapsing space into a <strong>Word Graph</strong> (the <strong>XRWG</strong>), augmented by Bib(s)Tex.</t>
|
||||
</blockquote><t>Instead of just throwing together all kinds media types into one experience (games), what about the intrinsic connections between them?<br />
|
||||
</blockquote><t>Instead of just throwing together all kinds media types into one experience (games), what about their tagged/semantical relationships?<br />
|
||||
|
||||
Why is HTML adopted less in games outside the browser?
|
||||
Through the lens of game-making, ideally metadata must come <strong>with</strong> that text, but not <strong>obfuscate</strong> the text, or <strong>spawning another request</strong> to fetch it.<br />
|
||||
Perhaps the following question is related: why is HTML adopted less in games outside the browser?
|
||||
Through the lens of constructive lazy game-developers, ideally metadata must come <strong>with</strong> text, but not <strong>obfuscate</strong> the text, or <strong>spawning another request</strong> to fetch it.<br />
|
||||
|
||||
XR Fragments does this by detecting Bib(s)Tex, without introducing a new language or fileformat<br />
|
||||
</t>
|
||||
|
@ -526,10 +660,11 @@ XR Fragments does this by detecting Bib(s)Tex, without introducing a new languag
|
|||
<li>HTML/RDF/JSON is still great, but is beyond the XRWG-scope (they fit better in the application-layer)</li>
|
||||
<li>Applications don't have to be able to access the XRWG programmatically, as they can easily generate one themselves by traversing the scene-nodes.</li>
|
||||
<li>The XR Fragment focuses on fast and easy-to-generate end-user controllable word graphs (instead of complex implementations that try to defeat word ambiguity)</li>
|
||||
<li>Tags are the scope for now (supporting <eref target="https://github.com/WICG/scroll-to-text-fragment">https://github.com/WICG/scroll-to-text-fragment</eref> will be considered)</li>
|
||||
</ol>
|
||||
<t>Example:</t>
|
||||
|
||||
<artwork> http://y.io/z.fbx | Derived XRWG (shown as BibTex)
|
||||
<artwork> http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
| @house{castle,
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | url = {https://y.io/z.fbx#castle}
|
||||
|
@ -551,7 +686,7 @@ XR Fragments does this by detecting Bib(s)Tex, without introducing a new languag
|
|||
<blockquote><t>the <tt>#john@baroque</tt>-bib associates both text <tt>John</tt> and objectname <tt>john</tt>, with tag <tt>baroque</tt></t>
|
||||
</blockquote><t>Another example:</t>
|
||||
|
||||
<artwork> http://y.io/z.fbx | Derived XRWG (printed as BibTex)
|
||||
<artwork> http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
|
||||
----------------------------------------------------------------------------+--------------------------------------
|
||||
|
|
||||
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | @house{castle,
|
||||
|
@ -584,18 +719,18 @@ This allows hasslefree authoring and copy-paste of associations <strong>for and
|
|||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><tt>https://my.com/foo.gltf#.baroque</tt></td>
|
||||
<td>highlights mesh <tt>john</tt>, 3D mesh <tt>castle</tt>, text <tt>John built(..)</tt></td>
|
||||
<td><tt>https://my.com/foo.gltf#baroque</tt></td>
|
||||
<td>draws lines between mesh <tt>john</tt>, 3D mesh <tt>castle</tt>, text <tt>John built(..)</tt></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>https://my.com/foo.gltf#john</tt></td>
|
||||
<td>highlights mesh <tt>john</tt>, and the text <tt>John built (..)</tt></td>
|
||||
<td>draws lines between mesh <tt>john</tt>, and the text <tt>John built (..)</tt></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><tt>https://my.com/foo.gltf#house</tt></td>
|
||||
<td>highlights mesh <tt>castle</tt>, and other objects with tag <tt>house</tt> or <tt>todo</tt></td>
|
||||
<td>draws lines between mesh <tt>castle</tt>, and other objects with tag <tt>house</tt> or <tt>todo</tt></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><blockquote><t><eref target="https://github.com/coderofsalvation/hashtagbibs">hashtagbibs</eref> potentially allow the enduser to annotate text/objects by <strong>speaking/typing/scanning associations</strong>, which the XR Browser saves to remotestorage (or localStorage per toplevel URL). As well as, referencing BibTags per URI later on: <tt>https://y.io/z.fbx#@baroque@todo</tt> e.g.</t>
|
||||
|
@ -607,7 +742,7 @@ This allows hasslefree authoring and copy-paste of associations <strong>for and
|
|||
<li>wordmatch object-names</li>
|
||||
<li>wordmatch object-tagnames</li>
|
||||
</ul>
|
||||
<t>Spatial wires can be rendered, words/objects can be highlighted/scaled etc.<br />
|
||||
<t>Spatial wires can be rendered between words/objects etc.<br />
|
||||
|
||||
Some pointers for good UX (but not necessary to be XR Fragment compatible):</t>
|
||||
|
||||
|
@ -805,6 +940,14 @@ here are some hashtagbibs followed by bibtex:
|
|||
</ul>
|
||||
</section>
|
||||
|
||||
<section anchor="faq"><name>FAQ</name>
|
||||
<t><strong>Q:</strong> Why is everything HTTP GET-based, what about POST/PUT/DELETE HATEOS<br />
|
||||
|
||||
<strong>A:</strong> Because it's out of scope: XR Fragment specifies a read-only way to surf XR documents. These things belong in the application layer (for example, an XR Hypermedia browser can decide to support POST/PUT/DELETE requests for embedded HTML thru <tt>src</tt> values)</t>
|
||||
<t><strong>Q:</strong> Why isn't there support for scripting
|
||||
<strong>A:</strong> This is out of scope, and up to the XR hypermedia browser. Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents). In order to prevent this backward-movement (hypermedia tends to liberate people from finnicky scripting) XR Fragments should never unhyperify itself by hardcoupling to a particular markup or scripting language. <eref target="https://xrfragment.org/doc/RFC_XR_Macros.html">XR Macro's</eref> are an example of something which is probably smarter and safer for hypermedia browsers to implement, instead of going full-in with a turing-complete scripting language (and suffer the security consequences later).</t>
|
||||
</section>
|
||||
|
||||
<section anchor="iana-considerations"><name>IANA Considerations</name>
|
||||
<t>This document has no IANA actions.</t>
|
||||
</section>
|
||||
|
@ -868,6 +1011,16 @@ here are some hashtagbibs followed by bibtex:
|
|||
<td>positions camera, triggers scene-preset/time</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>teleportation</td>
|
||||
<td>repositioning the enduser to a different position (or 3D scene/file)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>sourceportation</td>
|
||||
<td>teleporting the enduser to the original XR Document of an <tt>src</tt> embedded object.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>placeholder object</td>
|
||||
<td>a 3D object which with src-metadata (which will be replaced by the src-data.)</td>
|
||||
|
|
|
@ -96,7 +96,7 @@ Therefore, XR Macros allows us to enrich/connect existing dataformats, by offeri
|
|||
|
||||
<ol>
|
||||
<li>getting/setting common used 3D properties using querystring- or JSON-notation</li>
|
||||
<li>querying 3D properties using the lightweight searchengine notation used in <a href="https://xrfragment.org">XR Fragments</a></li>
|
||||
<li>targeting 3D properties using the lightweight query notation present in <a href="https://xrfragment.org">XR Fragments</a></li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
|
@ -107,10 +107,17 @@ Therefore, XR Macros allows us to enrich/connect existing dataformats, by offeri
|
|||
|
||||
<ol>
|
||||
<li>XR Macros use querystrings, but are HTML-agnostic (though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript).</li>
|
||||
<li>XR Macros represents setting/getting common used properties found in all popular 3D frameworks/(game)editors/internet browsers.</li>
|
||||
<li>XR Macros acts as simple eventhandlers for URI Fragments</li>
|
||||
<li>An XR Macro is 3D metadata which starts with ‘!’ (<code>!clickme: fog=0,10</code> e.g.)</li>
|
||||
<li>Metadata-values can contain the <code>|</code> symbol to 🎲 roundrobin variable values (<code>!toggleme: fog=0,10|fog=0,1000</code> e.g.)</li>
|
||||
<li>XR Macros acts as simple eventhandlers for URI Fragments: they are automatically published on the (<a href="https://xrfragment.org">XR Fragments</a>) hashbus, to act as events (so more serious scripting languages can react to them as well).</li>
|
||||
<li>XR Macros can assign object metadata (<code>!setlocal: foo=1</code> writes <code>foo:1</code> metadata to the object containing the <code>!setlocal</code> metadata)</li>
|
||||
<li>XR Macros can assign global metadata (<code>!setfoo: #foo=1</code> writes <code>foo:1</code> metadata to the root scene-node)</li>
|
||||
</ol>
|
||||
|
||||
<blockquote>
|
||||
<p>These very simple principles allow for rich interactions and dynamic querying</p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="conventions-and-definitions">Conventions and Definitions</h1>
|
||||
|
||||
<p>See appendix below in case certain terms are not clear.</p>
|
||||
|
@ -185,7 +192,7 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>!clickme</td>
|
||||
<td>!cycleme</td>
|
||||
<td>day|noon|night</td>
|
||||
<td>object clicked</td>
|
||||
</tr>
|
||||
|
@ -214,7 +221,7 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
<p>when a user clicks an object with the custom properties above, it should trigger either <code>day</code> <code>noon</code> or <code>night</code> in roundrobin fashion.</p>
|
||||
</blockquote>
|
||||
|
||||
<h2 id="usecase-click-object-uri-fragment-and-scene-load">Usecase: click object, URI fragment and scene load</h2>
|
||||
<h2 id="usecase-click-object-or-uri-fragment-and-scene-load-trigger">Usecase: click object or URI fragment, and scene load trigger</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -278,8 +285,8 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>!random</td>
|
||||
<td>day</td>
|
||||
<td>noon</td>
|
||||
<td>!day</td>
|
||||
<td>!noon</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
@ -303,7 +310,45 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
</table>
|
||||
|
||||
<blockquote>
|
||||
<p>The XR Browser should offer a contextmenu with these options when more than one <code>!</code>-macro is present on an object.</p>
|
||||
<p>When interacting with an object with more than one <code>!</code>-macro, the XR Browser should offer a contextmenu to execute a macro.</p>
|
||||
</blockquote>
|
||||
|
||||
<p>In a similar way, when <strong>any</strong> <code>!</code>-macro is present on the sceneroot, the XR Browser should offer a context-menu to execute those macro’s.</p>
|
||||
|
||||
<h2 id="event-bubble-flow">Event Bubble-flow</h2>
|
||||
|
||||
<p>click object with (<code>!clickme</code>:<code>AR</code> or <code>!clickme</code>: <code>!reset</code> e.g.)</p>
|
||||
|
||||
<pre><code> ◻
|
||||
│
|
||||
└── does current object contain this property-key (`AR` or `!reset` e.g.)?
|
||||
└── no: is there any (root)object containing property `AR`
|
||||
└── yes: evaluate its (roundrobin) XR macro-value(s) (and exit)
|
||||
└── no: trigger URL: #AR
|
||||
</code></pre>
|
||||
|
||||
<p>click object with (<code>!clickme</code>:<code>#AR|#VR</code> e.g.)</p>
|
||||
|
||||
<pre><code> ◻
|
||||
│
|
||||
└── apply the roundrobin (rotate the options, value `#AR` becomes `#VR` upon next click)
|
||||
└── is there any object with property-key (`#AR` e.g.)?
|
||||
└── no: just update the URL to `#AR`
|
||||
└── yes: apply its value to the scene, and update the URL to `#AR`
|
||||
|
||||
click object with (`!clickme`:`!foo|!bar|!flop` e.g.)
|
||||
</code></pre>
|
||||
|
||||
<p>◻
|
||||
│<br>
|
||||
└── apply the roundrobin (rotate the options, value <code>!foo</code> becomes <code>!bar</code> upon next click)
|
||||
└── is there any object with property-key (<code>!foo</code> e.g.)?
|
||||
└── no: do nothing
|
||||
└── yes: apply its value to the scene
|
||||
“`</p>
|
||||
|
||||
<blockquote>
|
||||
<p>Note that only macro’s can trigger roundrobin values or contextmenu’s, as well as roundrobin values never ending up in the toplevel URL.</p>
|
||||
</blockquote>
|
||||
|
||||
<h1 id="security-considerations">Security Considerations</h1>
|
||||
|
|
|
@ -115,8 +115,13 @@ Therefore, XR Macros allows us to enrich/connect existing dataformats, by offeri
|
|||
# Core principle
|
||||
|
||||
1. XR Macros use querystrings, but are HTML-agnostic (though pseudo-XR Fragment browsers **can** be implemented on top of HTML/Javascript).
|
||||
1. XR Macros represents setting/getting common used properties found in all popular 3D frameworks/(game)editors/internet browsers.
|
||||
1. XR Macros acts as simple eventhandlers for URI Fragments
|
||||
1. An XR Macro is 3D metadata which starts with '!' (`!clickme: fog=0,10` e.g.)
|
||||
1. Metadata-values can contain the `|` symbol to 🎲 roundrobin variable values (`!toggleme: fog=0,10|fog=0,1000` e.g.)
|
||||
1. XR Macros acts as simple eventhandlers for URI Fragments: they are automatically published on the ([XR Fragments](https://xrfragment.org)) hashbus, to act as events (so more serious scripting languages can react to them as well).
|
||||
1. XR Macros can assign object metadata (`!setlocal: foo=1` writes `foo:1` metadata to the object containing the `!setlocal` metadata)
|
||||
1. XR Macros can assign global metadata (`!setfoo: #foo=1` writes `foo:1` metadata to the root scene-node)
|
||||
|
||||
> These very simple principles allow for rich interactions and dynamic querying
|
||||
|
||||
# Conventions and Definitions
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
|
||||
Internet Engineering Task Force L.R. van Kammen
|
||||
Internet-Draft 7 September 2023
|
||||
Internet-Draft 21 September 2023
|
||||
Intended status: Informational
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ Status of This Memo
|
|||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on 10 March 2024.
|
||||
This Internet-Draft will expire on 24 March 2024.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
|
@ -53,7 +53,7 @@ Copyright Notice
|
|||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 1]
|
||||
van Kammen Expires 24 March 2024 [Page 1]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
@ -71,12 +71,14 @@ Table of Contents
|
|||
4.1. Usecase: click object . . . . . . . . . . . . . . . . . . 3
|
||||
4.2. Usecase: conditional click object . . . . . . . . . . . . 3
|
||||
4.3. Usecase: click object (roundrobin) . . . . . . . . . . . 4
|
||||
4.4. Usecase: click object, URI fragment and scene load . . . 4
|
||||
4.5. Usecase: present context menu with options . . . . . . . 4
|
||||
5. Security Considerations . . . . . . . . . . . . . . . . . . . 5
|
||||
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 5
|
||||
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
8. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 5
|
||||
4.4. Usecase: click object or URI fragment, and scene load
|
||||
trigger . . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
4.5. Usecase: present context menu with options . . . . . . . 5
|
||||
4.6. Event Bubble-flow . . . . . . . . . . . . . . . . . . . . 5
|
||||
5. Security Considerations . . . . . . . . . . . . . . . . . . . 6
|
||||
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 6
|
||||
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 6
|
||||
8. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 6
|
||||
|
||||
1. Introduction
|
||||
|
||||
|
@ -91,8 +93,8 @@ Table of Contents
|
|||
|
||||
1. getting/setting common used 3D properties using querystring- or
|
||||
JSON-notation
|
||||
2. querying 3D properties using the lightweight searchengine
|
||||
notation used in XR Fragments (https://xrfragment.org)
|
||||
2. targeting 3D properties using the lightweight query notation
|
||||
present in XR Fragments (https://xrfragment.org)
|
||||
|
||||
| NOTE: The chapters in this document are ordered from highlevel to
|
||||
| lowlevel (technical) as much as possible
|
||||
|
@ -102,18 +104,30 @@ Table of Contents
|
|||
1. XR Macros use querystrings, but are HTML-agnostic (though pseudo-
|
||||
XR Fragment browsers *can* be implemented on top of HTML/
|
||||
Javascript).
|
||||
2. XR Macros represents setting/getting common used properties found
|
||||
in all popular 3D frameworks/(game)editors/internet browsers.
|
||||
3. XR Macros acts as simple eventhandlers for URI Fragments
|
||||
2. An XR Macro is 3D metadata which starts with '!' (!clickme:
|
||||
fog=0,10 e.g.)
|
||||
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 2]
|
||||
van Kammen Expires 24 March 2024 [Page 2]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
3. Metadata-values can contain the | symbol to 🎲 roundrobin
|
||||
variable values (!toggleme: fog=0,10|fog=0,1000 e.g.)
|
||||
4. XR Macros acts as simple eventhandlers for URI Fragments: they
|
||||
are automatically published on the (XR Fragments
|
||||
(https://xrfragment.org)) hashbus, to act as events (so more
|
||||
serious scripting languages can react to them as well).
|
||||
5. XR Macros can assign object metadata (!setlocal: foo=1 writes
|
||||
foo:1 metadata to the object containing the !setlocal metadata)
|
||||
6. XR Macros can assign global metadata (!setfoo: #foo=1 writes
|
||||
foo:1 metadata to the root scene-node)
|
||||
|
||||
| These very simple principles allow for rich interactions and
|
||||
| dynamic querying
|
||||
|
||||
3. Conventions and Definitions
|
||||
|
||||
See appendix below in case certain terms are not clear.
|
||||
|
@ -148,6 +162,14 @@ Internet-Draft XR Macros September 2023
|
|||
| # | foo=1 | scene |
|
||||
+-----------------+------------------+----------------------------+
|
||||
| !clickme | q=foo>2&bg=1,1,1 | object clicked and foo > 2 |
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 24 March 2024 [Page 3]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
+-----------------+------------------+----------------------------+
|
||||
|
||||
Table 2
|
||||
|
@ -156,26 +178,12 @@ Internet-Draft XR Macros September 2023
|
|||
| should set the backgroundcolor to 1,1,1 when foo is greater than 2
|
||||
| (see previous example)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 3]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
4.3. Usecase: click object (roundrobin)
|
||||
|
||||
+=================+================+================+
|
||||
| custom property | value | trigger when |
|
||||
+=================+================+================+
|
||||
| !clickme | day|noon|night | object clicked |
|
||||
| !cycleme | day|noon|night | object clicked |
|
||||
+-----------------+----------------+----------------+
|
||||
| day | bg=1,1,1 | roundrobin |
|
||||
+-----------------+----------------+----------------+
|
||||
|
@ -189,7 +197,7 @@ Internet-Draft XR Macros September 2023
|
|||
| when a user clicks an object with the custom properties above, it
|
||||
| should trigger either day noon or night in roundrobin fashion.
|
||||
|
||||
4.4. Usecase: click object, URI fragment and scene load
|
||||
4.4. Usecase: click object or URI fragment, and scene load trigger
|
||||
|
||||
+=================+================+======================+
|
||||
| custom property | value | trigger when |
|
||||
|
@ -209,23 +217,24 @@ Internet-Draft XR Macros September 2023
|
|||
|
||||
Table 4
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 24 March 2024 [Page 4]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
4.5. Usecase: present context menu with options
|
||||
|
||||
+=================+================+========================+
|
||||
| custom property | value | trigger when |
|
||||
+=================+================+========================+
|
||||
| !random | day | noon |
|
||||
| !random | !day | !noon |
|
||||
+-----------------+----------------+------------------------+
|
||||
| !day | bg=1,1,1 | clicked in contextmenu |
|
||||
+-----------------+----------------+------------------------+
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 4]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
| !noon | bg=0.5,0.5,0.5 | clicked in contextmenu |
|
||||
+-----------------+----------------+------------------------+
|
||||
| !night | bg=0,0,0&foo=2 | clicked in contextmenu |
|
||||
|
@ -233,8 +242,49 @@ Internet-Draft XR Macros September 2023
|
|||
|
||||
Table 5
|
||||
|
||||
| The XR Browser should offer a contextmenu with these options when
|
||||
| more than one !-macro is present on an object.
|
||||
| When interacting with an object with more than one !-macro, the XR
|
||||
| Browser should offer a contextmenu to execute a macro.
|
||||
|
||||
In a similar way, when *any* !-macro is present on the sceneroot, the
|
||||
XR Browser should offer a context-menu to execute those macro's.
|
||||
|
||||
4.6. Event Bubble-flow
|
||||
|
||||
click object with (!clickme:AR or !clickme: !reset e.g.)
|
||||
|
||||
◻
|
||||
│
|
||||
└── does current object contain this property-key (`AR` or `!reset` e.g.)?
|
||||
└── no: is there any (root)object containing property `AR`
|
||||
└── yes: evaluate its (roundrobin) XR macro-value(s) (and exit)
|
||||
└── no: trigger URL: #AR
|
||||
|
||||
click object with (!clickme:#AR|#VR e.g.)
|
||||
|
||||
◻
|
||||
│
|
||||
└── apply the roundrobin (rotate the options, value `#AR` becomes `#VR` upon next click)
|
||||
└── is there any object with property-key (`#AR` e.g.)?
|
||||
└── no: just update the URL to `#AR`
|
||||
└── yes: apply its value to the scene, and update the URL to `#AR`
|
||||
|
||||
click object with (`!clickme`:`!foo|!bar|!flop` e.g.)
|
||||
|
||||
◻ │
|
||||
└── apply the roundrobin (rotate the options, value !foo becomes !bar
|
||||
upon next click) └── is there any object with property-key (!foo
|
||||
e.g.)? └── no: do nothing └── yes: apply its value to the scene ```
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 24 March 2024 [Page 5]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
| Note that only macro's can trigger roundrobin values or
|
||||
| contextmenu's, as well as roundrobin values never ending up in the
|
||||
| toplevel URL.
|
||||
|
||||
5. Security Considerations
|
||||
|
||||
|
@ -274,14 +324,6 @@ Internet-Draft XR Macros September 2023
|
|||
| (un)obtrusive | obtrusive: wrapping human text/thought in |
|
||||
| | XML/HTML/JSON obfuscates human text into |
|
||||
| | a salad of machine-symbols and words |
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 5]
|
||||
|
||||
Internet-Draft XR Macros September 2023
|
||||
|
||||
|
||||
+---------------+---------------------------------------------------+
|
||||
|
||||
Table 6
|
||||
|
@ -291,46 +333,4 @@ Internet-Draft XR Macros September 2023
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
van Kammen Expires 10 March 2024 [Page 6]
|
||||
van Kammen Expires 24 March 2024 [Page 6]
|
||||
|
|
|
@ -32,7 +32,7 @@ Therefore, XR Macros allows us to enrich/connect existing dataformats, by offeri
|
|||
|
||||
<ol spacing="compact">
|
||||
<li>getting/setting common used 3D properties using querystring- or JSON-notation</li>
|
||||
<li>querying 3D properties using the lightweight searchengine notation used in <eref target="https://xrfragment.org">XR Fragments</eref></li>
|
||||
<li>targeting 3D properties using the lightweight query notation present in <eref target="https://xrfragment.org">XR Fragments</eref></li>
|
||||
</ol>
|
||||
<blockquote><t>NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible</t>
|
||||
</blockquote></section>
|
||||
|
@ -41,10 +41,14 @@ Therefore, XR Macros allows us to enrich/connect existing dataformats, by offeri
|
|||
|
||||
<ol spacing="compact">
|
||||
<li>XR Macros use querystrings, but are HTML-agnostic (though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript).</li>
|
||||
<li>XR Macros represents setting/getting common used properties found in all popular 3D frameworks/(game)editors/internet browsers.</li>
|
||||
<li>XR Macros acts as simple eventhandlers for URI Fragments</li>
|
||||
<li>An XR Macro is 3D metadata which starts with '!' (<tt>!clickme: fog=0,10</tt> e.g.)</li>
|
||||
<li>Metadata-values can contain the <tt>|</tt> symbol to 🎲 roundrobin variable values (<tt>!toggleme: fog=0,10|fog=0,1000</tt> e.g.)</li>
|
||||
<li>XR Macros acts as simple eventhandlers for URI Fragments: they are automatically published on the (<eref target="https://xrfragment.org">XR Fragments</eref>) hashbus, to act as events (so more serious scripting languages can react to them as well).</li>
|
||||
<li>XR Macros can assign object metadata (<tt>!setlocal: foo=1</tt> writes <tt>foo:1</tt> metadata to the object containing the <tt>!setlocal</tt> metadata)</li>
|
||||
<li>XR Macros can assign global metadata (<tt>!setfoo: #foo=1</tt> writes <tt>foo:1</tt> metadata to the root scene-node)</li>
|
||||
</ol>
|
||||
</section>
|
||||
<blockquote><t>These very simple principles allow for rich interactions and dynamic querying</t>
|
||||
</blockquote></section>
|
||||
|
||||
<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
|
||||
<t>See appendix below in case certain terms are not clear.</t>
|
||||
|
@ -117,7 +121,7 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>!clickme</td>
|
||||
<td>!cycleme</td>
|
||||
<td>day|noon|night</td>
|
||||
<td>object clicked</td>
|
||||
</tr>
|
||||
|
@ -143,7 +147,7 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
</table><blockquote><t>when a user clicks an object with the custom properties above, it should trigger either <tt>day</tt> <tt>noon</tt> or <tt>night</tt> in roundrobin fashion.</t>
|
||||
</blockquote></section>
|
||||
|
||||
<section anchor="usecase-click-object-uri-fragment-and-scene-load"><name>Usecase: click object, URI fragment and scene load</name>
|
||||
<section anchor="usecase-click-object-or-uri-fragment-and-scene-load-trigger"><name>Usecase: click object or URI fragment, and scene load trigger</name>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -205,8 +209,8 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>!random</td>
|
||||
<td>day</td>
|
||||
<td>noon</td>
|
||||
<td>!day</td>
|
||||
<td>!noon</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
@ -227,7 +231,39 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
<td>clicked in contextmenu</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><blockquote><t>The XR Browser should offer a contextmenu with these options when more than one <tt>!</tt>-macro is present on an object.</t>
|
||||
</table><blockquote><t>When interacting with an object with more than one <tt>!</tt>-macro, the XR Browser should offer a contextmenu to execute a macro.</t>
|
||||
</blockquote><t>In a similar way, when <strong>any</strong> <tt>!</tt>-macro is present on the sceneroot, the XR Browser should offer a context-menu to execute those macro's.</t>
|
||||
</section>
|
||||
|
||||
<section anchor="event-bubble-flow"><name>Event Bubble-flow</name>
|
||||
<t>click object with (<tt>!clickme</tt>:<tt>AR</tt> or <tt>!clickme</tt>: <tt>!reset</tt> e.g.)</t>
|
||||
|
||||
<artwork> ◻
|
||||
│
|
||||
└── does current object contain this property-key (`AR` or `!reset` e.g.)?
|
||||
└── no: is there any (root)object containing property `AR`
|
||||
└── yes: evaluate its (roundrobin) XR macro-value(s) (and exit)
|
||||
└── no: trigger URL: #AR
|
||||
</artwork>
|
||||
<t>click object with (<tt>!clickme</tt>:<tt>#AR|#VR</tt> e.g.)</t>
|
||||
|
||||
<artwork> ◻
|
||||
│
|
||||
└── apply the roundrobin (rotate the options, value `#AR` becomes `#VR` upon next click)
|
||||
└── is there any object with property-key (`#AR` e.g.)?
|
||||
└── no: just update the URL to `#AR`
|
||||
└── yes: apply its value to the scene, and update the URL to `#AR`
|
||||
|
||||
click object with (`!clickme`:`!foo|!bar|!flop` e.g.)
|
||||
</artwork>
|
||||
<t>◻
|
||||
│<br />
|
||||
└── apply the roundrobin (rotate the options, value <tt>!foo</tt> becomes <tt>!bar</tt> upon next click)
|
||||
└── is there any object with property-key (<tt>!foo</tt> e.g.)?
|
||||
└── no: do nothing
|
||||
└── yes: apply its value to the scene
|
||||
```</t>
|
||||
<blockquote><t>Note that only macro's can trigger roundrobin values or contextmenu's, as well as roundrobin values never ending up in the toplevel URL.</t>
|
||||
</blockquote></section>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
mmark RFC_XR_Fragments.md > RFC_XR_Fragments.xml
|
||||
mmark --html RFC_XR_Fragments.md | grep -vE '(<!--{|}-->)' > RFC_XR_Fragments.html
|
||||
xml2rfc --v3 RFC_XR_Fragments.xml # RFC_XR_Fragments.txt
|
||||
sed -i 's/Expires: .*//g' RFC_XR_Fragments.txt
|
||||
for topic in Fragments Macros; do
|
||||
mmark RFC_XR_$topic.md > RFC_XR_$topic.xml
|
||||
mmark --html RFC_XR_$topic.md | grep -vE '(<!--{|}-->)' > RFC_XR_$topic.html
|
||||
xml2rfc --v3 RFC_XR_$topic.xml # RFC_XR_$topic.txt
|
||||
sed -i 's/Expires: .*//g' RFC_XR_$topic.txt
|
||||
done
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
})
|
||||
|
||||
// add screenshot component with camera to capture bigger size equirects
|
||||
// document.querySelector('a-scene').components.screenshot.capture('perspective')
|
||||
$('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
|
||||
|
||||
if( window.outerWidth > 800 )
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -50,33 +50,25 @@
|
|||
<label for="fragment">Property in 3D file</label>
|
||||
<br><br>
|
||||
<select id="fragment" class="w-100">
|
||||
<optgroup label="asset loading / linking">
|
||||
<option x="1">prio</option>
|
||||
<optgroup label="default / predefined view">
|
||||
<option x="pos=0,0,1&fov=2">#</option>
|
||||
<option x="foo">class</option>
|
||||
<option x="">#mypredefinedview</option>
|
||||
</optgroup>
|
||||
<optgroup label="tagging / lazyloading content">
|
||||
<option x="foo">tag</option>
|
||||
<option x="./2.gtlf">src</option>
|
||||
</optgroup>
|
||||
<optgroup label="href navigation / portals / teleporting">
|
||||
<option x="1,2,0">pos</option>
|
||||
<option x="3.gltf#q=.kitchen">href</option>
|
||||
</optgroup>
|
||||
<optgroup label="query selector / object manipulation">
|
||||
<option x=".summer -.winter cube" selected="selected" default>q</option>
|
||||
<option x="2" >scale</option>
|
||||
<option x="1,2,3">rot</option>
|
||||
<option x="1,1,1">translate</option>
|
||||
<option x="1">visible</option>
|
||||
<option x="3.gltf#q=.kitchen">href</option>
|
||||
</optgroup>
|
||||
<optgroup label="animation">
|
||||
<option x="1,200">t</option>
|
||||
<option x="0,-9.8,0">gravity</option>
|
||||
<option x="0.2,1">physics</option>
|
||||
<option x=#scroll=1,0|2s">scroll</option>
|
||||
</optgroup>
|
||||
<optgroup label="device / viewport settings">
|
||||
<option x="90">fov</option>
|
||||
<option x="1,100">clip</option>
|
||||
<option x="5m|FFAACC">fog</option>
|
||||
<option x="45">fov</option>
|
||||
</optgroup>
|
||||
<optgroup label="author / metadata">
|
||||
<option x="XXX">namespace</option>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div id="overlay" x-data="{ urls: ['#pos=0,1.6,15','#pos=0,1.6,15&rot=0,360,0'] }">
|
||||
<img src="./../../assets/logo.png" class="logo"/>
|
||||
<input type="submit" value="load 3D asset"></input>
|
||||
<input type="text" id="uri" value="" onchange="AFRAME.XRF.navigator.to( $('#uri').value )"/>
|
||||
<input type="text" id="uri" value="" onchange="XRF.navigator.to( $('#uri').value )"/>
|
||||
</div>
|
||||
<a class="btn-foot" id="source" target="_blank" href="https://github.com/coderofsalvation/xrfragment-helloworld">➕ clone project</a>
|
||||
<a class="btn-foot" id="embed" target="_blank" onclick="embed()">📺 embed</a>
|
||||
|
@ -63,7 +63,7 @@
|
|||
};
|
||||
|
||||
init();
|
||||
alert("for now, back-button (navigation) is only implemented in the AFRAME demo-implementation")
|
||||
notify("NOTE: only AFRAME demo has immersive back-button (for now)")
|
||||
|
||||
function init() {
|
||||
|
||||
|
@ -118,7 +118,7 @@
|
|||
|
||||
window.XRF = XRF // expose to form
|
||||
|
||||
let file = document.location.search.length > 2 ? document.location.search.substr(1) + document.location.hash : 'example.gltf#pos=1,0,4&rot=0,-30,0'
|
||||
let file = document.location.search.length > 2 ? document.location.search.substr(1) + document.location.hash : 'example.gltf#pos=0,1,20'
|
||||
|
||||
// optional decoration of href event
|
||||
XRF.addEventListener('href',(e) => {
|
||||
|
|
35
index.html
35
index.html
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,9 @@ xrf.frag = {}
|
|||
xrf.model = {}
|
||||
|
||||
xrf.init = ((init) => function(opts){
|
||||
let scene = new opts.THREE.Group()
|
||||
opts.scene.add(scene)
|
||||
opts.scene = scene
|
||||
init(opts)
|
||||
if( opts.loaders ) Object.values(opts.loaders).map( xrf.patchLoader )
|
||||
|
||||
|
@ -54,7 +57,7 @@ xrf.parseModel = function(model,url){
|
|||
model.mixer.update( model.clock.getDelta() )
|
||||
|
||||
// update focusline
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime() ))/2
|
||||
xrf.focusLine.material.color.r = (1.0 + Math.sin( model.clock.getElapsedTime()*10 ))/2
|
||||
xrf.focusLine.material.dashSize = 0.2 + 0.02*Math.sin( model.clock.getElapsedTime() )
|
||||
xrf.focusLine.material.gapSize = 0.1 + 0.02*Math.sin( model.clock.getElapsedTime() *3 )
|
||||
xrf.focusLine.material.opacity = 0.25 + 0.15*Math.sin( model.clock.getElapsedTime() * 3 )
|
||||
|
@ -79,7 +82,8 @@ xrf.reset = () => {
|
|||
xrf.scene.traverse( (child) => child.isXRF ? nodes.push(child) : false )
|
||||
nodes.map( disposeObject ) // leave non-XRF objects intact
|
||||
xrf.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.add( xrf.interactive)
|
||||
xrf.add( xrf.interactive )
|
||||
xrf.layers = 0
|
||||
}
|
||||
|
||||
xrf.parseUrl = (url) => {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
xrf.frag.href = function(v, opts){
|
||||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( mesh.userData.XRF.href.exec ) return // mesh already initialized
|
||||
|
||||
|
@ -73,7 +73,7 @@ xrf.frag.href = function(v, opts){
|
|||
varying float vDistance;
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition);
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition );
|
||||
vec2 sampleUV;
|
||||
sampleUV.y = -clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
|
||||
|
@ -81,7 +81,7 @@ xrf.frag.href = function(v, opts){
|
|||
vec4 color = texture2D(pano, sampleUV);
|
||||
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js)
|
||||
float luminance = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
vec4 grayscale_color = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
|
||||
gl_FragColor = grayscale_color;
|
||||
}
|
||||
`,
|
||||
|
@ -92,14 +92,15 @@ xrf.frag.href = function(v, opts){
|
|||
let click = mesh.userData.XRF.href.exec = (e) => {
|
||||
let isLocal = v.string[0] == '#'
|
||||
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( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,88 +6,125 @@ xrf.frag.src = function(v, opts){
|
|||
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
|
||||
|
||||
console.log(" └ instancing src")
|
||||
let src = new THREE.Group()
|
||||
let frag = xrfragment.URI.parse(v.string)
|
||||
let src;
|
||||
let url = v.string
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
||||
|
||||
const localSRC = () => {
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
xrf.frag.src.eval( src, opts )
|
||||
mesh.add( src )
|
||||
const addScene = (scene,url,frag) => {
|
||||
src = xrf.frag.src.filterScene(scene,{...opts,frag})
|
||||
xrf.frag.src.scale( src, opts, url )
|
||||
xrf.frag.src.eval( src, opts, url )
|
||||
mesh.add(src)
|
||||
if( mesh.material ) mesh.material.visible = false
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
fetch(v.string, { method: 'HEAD' })
|
||||
const externalSRC = (url,frag,src) => {
|
||||
fetch(url, { method: 'HEAD' })
|
||||
.then( (res) => {
|
||||
console.log(`loading src ${v.string}`)
|
||||
console.log(`loading src ${url}`)
|
||||
let mimetype = res.headers.get('Content-type')
|
||||
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
|
||||
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
|
||||
console.log("src mimetype: "+mimetype)
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](v.string,opts) : xrf.frag.src.type.unknown(v.string,opts)
|
||||
opts = { ...opts, src, frag }
|
||||
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
|
||||
})
|
||||
.finally( () => {
|
||||
.then( (model) => {
|
||||
if( model && model.scene ) addScene(model.scene, url, frag )
|
||||
})
|
||||
.finally( () => { })
|
||||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
if( url[0] == "#" ) addScene(scene,url,frag) // current file
|
||||
else externalSRC(url,frag) // external file
|
||||
}
|
||||
|
||||
xrf.frag.src.eval = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE, hashbus} = opts
|
||||
if( url ){
|
||||
let frag = xrfragment.URI.parse(url)
|
||||
// scale URI XR Fragments (queries) inside src-value
|
||||
for( var i in frag ){
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
}
|
||||
hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
console.log(mesh.name+" url="+url)
|
||||
console.dir(mesh)
|
||||
//let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
//let frag = xrfragment.URI.parse(url)
|
||||
//// scale URI XR Fragments (queries) inside src-value
|
||||
//for( var i in frag ){
|
||||
// hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene}))
|
||||
//}
|
||||
//hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist)
|
||||
//hashbus.pub( url, {scene} ) // and eval URI XR fragments
|
||||
}
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
xrf.frag.src.scale = function(scene, opts, url){
|
||||
let { mesh, model, camera, renderer, THREE} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
if( restrictToBoundingBox ){
|
||||
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
if( restrictTo3DBoundingBox ){
|
||||
// spec 3 of https://xrfragment.org/#src
|
||||
// spec 1 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
// normalize instanced objectsize to boundingbox
|
||||
let bboxMesh = new THREE.Box3().setFromObject(mesh);
|
||||
let bboxScene = new THREE.Box3().setFromObject(scene);
|
||||
let maxScene = bboxScene.max.y > bboxScene.max.x ? bboxScene.max.y : bboxScene.max.x
|
||||
let maxMesh = bboxMesh.max.y > bboxMesh.max.x ? bboxMesh.max.y : bboxMesh.max.x
|
||||
let factor = maxMesh > maxScene ? maxScene / maxMesh : maxMesh / maxScene
|
||||
scene.scale.multiplyScalar( factor )
|
||||
let sizeFrom = new THREE.Vector3()
|
||||
let sizeTo = new THREE.Vector3()
|
||||
|
||||
let empty = new THREE.Object3D()
|
||||
|
||||
// *TODO* exclude invisible objects from boundingbox size-detection
|
||||
//
|
||||
// THREE.Box3.prototype.expandByObject = (function(expandByObject){
|
||||
// return function(object,precise){
|
||||
// return expandByObject.call(this, object.visible ? object : empty, precise)
|
||||
// }
|
||||
// })(THREE.Box3.prototype.expandByObject)
|
||||
|
||||
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
|
||||
new THREE.Box3().setFromObject(scene).getSize(sizeFrom)
|
||||
let ratio = sizeFrom.divide(sizeTo)
|
||||
scene.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z));
|
||||
// let factor = getMax(sizeTo) < getMax(sizeFrom) ? getMax(sizeTo) / getMax(sizeFrom) : getMax(sizeFrom) / getMax(sizeTo)
|
||||
// scene.scale.multiplyScalar( factor )
|
||||
}else{
|
||||
// spec 4 of https://xrfragment.org/#src
|
||||
// spec 2 of https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
console.log("normal scale: "+url)
|
||||
scene.scale.multiply( mesh.scale )
|
||||
}
|
||||
scene.isXRF = model.scene.isSRC = true
|
||||
if( !opts.recursive && mesh.material ) mesh.material.visible = false // lets hide the preview object because deleting disables animations+nested objs
|
||||
}
|
||||
|
||||
xrf.frag.src.filterScene = (scene,opts) => {
|
||||
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
|
||||
let obj, src
|
||||
console.dir(frag)
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
src = new THREE.Group()
|
||||
if( Object.keys(frag).length > 0 ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ){
|
||||
src.add( obj = scene.getObjectByName(i).clone(true) )
|
||||
}
|
||||
hashbus.pub.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
}else src = scene.clone(true)
|
||||
console.dir({name: mesh.name, scene, frag})
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone(true);
|
||||
src.isSRC = src.isXRF = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
src.isSRC = src.isXRF = true;
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
return src
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -110,27 +147,22 @@ xrf.frag.src.type['unknown'] = function( url, opts ){
|
|||
* mimetype: model/gltf+json
|
||||
*/
|
||||
|
||||
xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
||||
xrf.frag.src.type['gltf'] = function( url, opts ){
|
||||
return new Promise( (resolve,reject) => {
|
||||
let {mesh} = opts
|
||||
let {mesh,src} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let loader
|
||||
|
||||
const Loader = xrf.loaders[ext]
|
||||
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
|
||||
if( !dir.match("://") ){ // force relative path
|
||||
dir = dir[0] == '.' ? dir : `.${dir}`
|
||||
dir = dir[0] == './' ? dir : `./${dir}`
|
||||
loader = new Loader().setPath( dir )
|
||||
}else loader = new Loader()
|
||||
|
||||
const onLoad = (model) => {
|
||||
xrf.frag.src.scale( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
xrf.frag.src.eval( model.scene, {...opts, model, scene: model.scene}, url )
|
||||
mesh.add( model.scene )
|
||||
loader.load(url, (model) => {
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
loader.load(url, onLoad )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -142,7 +174,7 @@ xrf.frag.src.type['model/gltf+json'] = function( url, opts ){
|
|||
|
||||
xrf.frag.src.type['image/png'] = function(url,opts){
|
||||
let {mesh} = opts
|
||||
let restrictToBoundingBox = mesh.geometry
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
const texture = new THREE.TextureLoader().load( url );
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
|
|
Loading…
Reference in New Issue