cleanup and minor tweaks

This commit is contained in:
Leon van Kammen 2023-10-12 17:04:46 +02:00
parent f08c0acfd4
commit 7f57e0907f
27 changed files with 3216 additions and 2317 deletions

View File

@ -788,6 +788,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts)
.then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play()
})
@ -865,9 +867,10 @@ xrf.parseModel = function(model,url){
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 )) * xrf.focusLine.opacity;
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.3
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.2
if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
}
}
xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){
if( nocollide.tid ) return // ratelimit
_event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) )
scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 )
}
@ -1082,8 +1085,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
hashbus.pub( url, model ) // and eval URI XR fragments
// spec: 2. init metadata
let frag = hashbus.pub( url, model ) // and eval URI XR fragments
// spec: predefined view(s) from URL (https://xrfragment.org/#predefined_view)
setTimeout( () => { // give external objects some slack
xrf.frag.updatePredefinedView({model,scene:model.scene,frag})
},2000)
xrf.add( model.scene )
xrf.navigator.updateHash(hash)
resolve(model)
@ -1096,9 +1103,14 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
})
window.addEventListener('hashchange', function (e){
xrf.emit('hash', {hash: document.location.hash })
})
// this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group()
@ -1398,7 +1410,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 = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
gl_FragColor = grayscale_color;
}
`,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed
if( mesh.material ){
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
}
xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false
if( o.material ){
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
}
})
// update mouse cursor
if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
}
xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts
let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection
if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name
if( mesh.visible ){
if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag})
.then( () => {
const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) )
// create new selections
match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
if( w.key == `#${id}` ){
if( w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
}
}
w.nodes.map( (mesh) => {
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
})
}
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src")
console.dir(frag)
}else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) {
let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url
xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
console.dir({opts,frag})
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
})
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries
let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => {
for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
@ -1655,23 +1669,41 @@ xrf.frag.rot = function(v, opts){
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
console.log(" └ instancing src")
let src;
let url = v.string
let frag = xrfragment.URI.parse(url)
let vfrag = xrfragment.URI.parse(url)
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
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 )
enableSourcePortation(src)
mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false
}
const enableSourcePortation = (src) => {
if( vfrag.href || v.string[0] == '#' ) return
let scale = new THREE.Vector3()
let size = new THREE.Vector3()
mesh.getWorldScale(scale)
new THREE.Box3().setFromObject(src).getSize(size)
const geo = new THREE.SphereGeometry( Math.max(size.x, size.y, size.z) / scale.x, 10, 10 )
const mat = new THREE.MeshBasicMaterial()
mat.transparent = true
mat.roughness = 0.05
mat.metalness = 1
mat.opacity = 0
const cube = new THREE.Mesh( geo, mat )
console.log("todo: sourceportate")
//mesh.add(cube)
}
const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' })
.then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error )
}
if( url[0] == "#" ) addScene(scene,url,frag) // current file
else externalSRC(url,frag) // external file
if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,vfrag) // external file
}
xrf.frag.src.eval = function(scene, opts, url){
@ -1916,9 +1948,9 @@ window.AFRAME.registerComponent('xrf', {
let aScene = document.querySelector('a-scene')
let XRF = AFRAME.XRF = xrf.init({
THREE,
camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
debug: true,
loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?

View File

@ -788,6 +788,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts)
.then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play()
})
@ -865,9 +867,10 @@ xrf.parseModel = function(model,url){
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 )) * xrf.focusLine.opacity;
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.3
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.2
if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
}
}
xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){
if( nocollide.tid ) return // ratelimit
_event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) )
scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 )
}
@ -1082,8 +1085,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
hashbus.pub( url, model ) // and eval URI XR fragments
// spec: 2. init metadata
let frag = hashbus.pub( url, model ) // and eval URI XR fragments
// spec: predefined view(s) from URL (https://xrfragment.org/#predefined_view)
setTimeout( () => { // give external objects some slack
xrf.frag.updatePredefinedView({model,scene:model.scene,frag})
},2000)
xrf.add( model.scene )
xrf.navigator.updateHash(hash)
resolve(model)
@ -1096,9 +1103,14 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
})
window.addEventListener('hashchange', function (e){
xrf.emit('hash', {hash: document.location.hash })
})
// this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group()
@ -1398,7 +1410,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 = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
gl_FragColor = grayscale_color;
}
`,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed
if( mesh.material ){
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
}
xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false
if( o.material ){
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
}
})
// update mouse cursor
if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
}
xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts
let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection
if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name
if( mesh.visible ){
if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag})
.then( () => {
const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) )
// create new selections
match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
if( w.key == `#${id}` ){
if( w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
}
}
w.nodes.map( (mesh) => {
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
})
}
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src")
console.dir(frag)
}else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) {
let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url
xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
console.dir({opts,frag})
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
})
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries
let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => {
for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
@ -1655,23 +1669,41 @@ xrf.frag.rot = function(v, opts){
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
console.log(" └ instancing src")
let src;
let url = v.string
let frag = xrfragment.URI.parse(url)
let vfrag = xrfragment.URI.parse(url)
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
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 )
enableSourcePortation(src)
mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false
}
const enableSourcePortation = (src) => {
if( vfrag.href || v.string[0] == '#' ) return
let scale = new THREE.Vector3()
let size = new THREE.Vector3()
mesh.getWorldScale(scale)
new THREE.Box3().setFromObject(src).getSize(size)
const geo = new THREE.SphereGeometry( Math.max(size.x, size.y, size.z) / scale.x, 10, 10 )
const mat = new THREE.MeshBasicMaterial()
mat.transparent = true
mat.roughness = 0.05
mat.metalness = 1
mat.opacity = 0
const cube = new THREE.Mesh( geo, mat )
console.log("todo: sourceportate")
//mesh.add(cube)
}
const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' })
.then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error )
}
if( url[0] == "#" ) addScene(scene,url,frag) // current file
else externalSRC(url,frag) // external file
if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,vfrag) // external file
}
xrf.frag.src.eval = function(scene, opts, url){

View File

@ -788,6 +788,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts)
.then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play()
})
@ -865,9 +867,10 @@ xrf.parseModel = function(model,url){
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 )) * xrf.focusLine.opacity;
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.3
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.2
if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
}
}
xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){
if( nocollide.tid ) return // ratelimit
_event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) )
scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 )
}
@ -1082,8 +1085,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
hashbus.pub( url, model ) // and eval URI XR fragments
// spec: 2. init metadata
let frag = hashbus.pub( url, model ) // and eval URI XR fragments
// spec: predefined view(s) from URL (https://xrfragment.org/#predefined_view)
setTimeout( () => { // give external objects some slack
xrf.frag.updatePredefinedView({model,scene:model.scene,frag})
},2000)
xrf.add( model.scene )
xrf.navigator.updateHash(hash)
resolve(model)
@ -1096,9 +1103,14 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
})
window.addEventListener('hashchange', function (e){
xrf.emit('hash', {hash: document.location.hash })
})
// this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group()
@ -1398,7 +1410,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 = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
gl_FragColor = grayscale_color;
}
`,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed
if( mesh.material ){
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
}
xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false
if( o.material ){
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
}
})
// update mouse cursor
if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
}
xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts
let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection
if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name
if( mesh.visible ){
if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag})
.then( () => {
const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) )
// create new selections
match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
if( w.key == `#${id}` ){
if( w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
}
}
w.nodes.map( (mesh) => {
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
})
}
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src")
console.dir(frag)
}else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) {
let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url
xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
console.dir({opts,frag})
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
})
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries
let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => {
for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
@ -1655,23 +1669,41 @@ xrf.frag.rot = function(v, opts){
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
console.log(" └ instancing src")
let src;
let url = v.string
let frag = xrfragment.URI.parse(url)
let vfrag = xrfragment.URI.parse(url)
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
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 )
enableSourcePortation(src)
mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false
}
const enableSourcePortation = (src) => {
if( vfrag.href || v.string[0] == '#' ) return
let scale = new THREE.Vector3()
let size = new THREE.Vector3()
mesh.getWorldScale(scale)
new THREE.Box3().setFromObject(src).getSize(size)
const geo = new THREE.SphereGeometry( Math.max(size.x, size.y, size.z) / scale.x, 10, 10 )
const mat = new THREE.MeshBasicMaterial()
mat.transparent = true
mat.roughness = 0.05
mat.metalness = 1
mat.opacity = 0
const cube = new THREE.Mesh( geo, mat )
console.log("todo: sourceportate")
//mesh.add(cube)
}
const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' })
.then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error )
}
if( url[0] == "#" ) addScene(scene,url,frag) // current file
else externalSRC(url,frag) // external file
if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,vfrag) // external file
}
xrf.frag.src.eval = function(scene, opts, url){

View File

@ -80,7 +80,7 @@ value: draft-XRFRAGMENTS-leonvankammen-00
<h1 class="special" id="abstract">Abstract</h1>
<p>This draft is a specification for 4D URLs &amp; navigation, which links together space, time &amp; text together, for hypermedia browsers with- or without a network-connection.<br>
<p>This draft is a specification for 4D URLs &amp; <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> navigation, which links together space, time &amp; text together, for hypermedia browsers with- or without a network-connection.<br>
The specification promotes spatial addressibility, sharing, navigation, query-ing and annotating interactive (text)objects across for (XR) Browsers.<br>
XR Fragments allows us to enrich existing dataformats, by recursive use of existing proven technologies like <a href="https://en.wikipedia.org/wiki/URI_fragment">URI Fragments</a> and BibTags notation.<br></p>
@ -92,11 +92,11 @@ XR Fragments allows us to enrich existing dataformats, by recursive use of exist
<p>How can we add more features to existing text &amp; 3D scenes, without introducing new dataformats?<br>
Historically, there&rsquo;s many attempts to create the ultimate markuplanguage or 3D fileformat.<br>
Their lowest common denominator is: (co)authoring using plain text.<br>
The lowest common denominator is: describing/tagging/naming nodes using <strong>plain text</strong>.<br>
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>addressibility and <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> 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/&amp; 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>
@ -199,12 +199,12 @@ Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a
<p>Traditional webbrowsers can become 4D document-ready by:</p>
<ul>
<li>loading 3D assets (gltf/fbx e.g.) natively (with or without using HTML).</li>
<li><a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> loading 3D assets (gltf/fbx e.g.) natively (with or without using HTML).</li>
<li>allowing assets to publish hashtags to themselves (the scene) using the hashbus (like hashtags controlling the scrollbar).</li>
<li>collapsing the 3D scene to an wordgraph (for essential navigation purposes) controllable thru a hash(tag)bus</li>
</ul>
<p>XR Fragments itself are HTML-agnostic, though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript.</p>
<p>XR Fragments itself are <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> and HTML-agnostic, though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript.</p>
<h1 id="conventions-and-definitions">Conventions and Definitions</h1>
@ -275,9 +275,9 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<tr>
<td><code>#t</code></td>
<td>vector2</td>
<td><code>#t=500,1000</code></td>
<td>sets animation-loop range between frame 500 and 1000</td>
<td>vector3</td>
<td><code>#t=1,500,1000</code></td>
<td>play animation-loop range between frame 500 and 1000, at normal speed</td>
</tr>
<tr>
@ -307,22 +307,6 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td>string</td>
<td><code>&quot;name&quot;: &quot;cube&quot;</code></td>
<td>identify/tag</td>
<td>object supported in all 3D fileformats &amp; scenes</td>
</tr>
<tr>
<td><code>tag</code></td>
<td>string</td>
<td><code>&quot;tag&quot;: &quot;cubes geo&quot;</code></td>
<td>tag object</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><code>href</code></td>
<td>string</td>
@ -338,16 +322,26 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<td>XR embed / teleport</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><code>tag</code></td>
<td>string</td>
<td><code>&quot;tag&quot;: &quot;cubes geo&quot;</code></td>
<td>tag object (for query-use / XRWG highlighting)</td>
<td>custom property in 3D fileformats</td>
</tr>
</tbody>
</table>
<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>
<p>NOTE: XR Fragments are optional but also file- and protocol-agnostic, which means that programmatic 3D scene(nodes) can also use the mechanism/metadata.</p>
</blockquote>
<h1 id="spatial-referencing-3d">Spatial Referencing 3D</h1>
<p>XR Fragments assume the following objectname-to-URIFragment mapping:</p>
<pre><code>
my.io/scene.fbx
+─────────────────────────────+
@ -366,6 +360,10 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</code></pre>
<blockquote>
<p>Every 3D fileformat supports named 3D object, and this name allows URLs (fragments) to reference them (and their children objects).</p>
</blockquote>
<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>
@ -375,7 +373,7 @@ For example, to render a portal with a preview-version of the scene, create an 3
</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>
<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. Basically an embedded link becoming an outbound link by activating it.</p>
</blockquote>
<h1 id="navigating-3d">Navigating 3D</h1>
@ -398,8 +396,8 @@ For example, to render a portal with a preview-version of the scene, create an 3
<tr>
<td><b>#t</b>=0,100</td>
<td>vector2</td>
<td>(re)position looprange of scene-animation or <code>src</code>-mediacontent</td>
<td>vector3</td>
<td>set playback speed, and (re)position looprange of scene-animation or <code>src</code>-mediacontent</td>
</tr>
<tr>
@ -416,7 +414,7 @@ For example, to render a portal with a preview-version of the scene, create an 3
<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 &lsquo;STOP&rsquo; at certain frame)</li>
<li><code>t</code> sets the playbackspeed and animation-range of the current scene animation(s) or <code>src</code>-mediacontent (video/audioframes e.g., use <code>t=0,7,7</code> to &lsquo;STOP&rsquo; at frame 7 e.g.)</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>
@ -533,7 +531,7 @@ Resizing will be happen accordingly to its placeholder object <code>aquariumcube
<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>
<h1 id="navigating-content-href-portals">Navigating content (href portals)</h1>
<h1 id="navigating-content-internal-outbound-href-portals">Navigating content (internal/outbound href portals)</h1>
<p>navigation, portals &amp; mutations</p>
@ -556,7 +554,7 @@ Resizing will be happen accordingly to its placeholder object <code>aquariumcube
</table>
<ol>
<li><p>clicking an &ldquo;external&rdquo;- or &ldquo;file URI&rdquo; fully replaces the current scene and assumes <code>pos=0,0,0&amp;rot=0,0,0</code> by default (unless specified)</p></li>
<li><p>clicking an outbound &ldquo;external&rdquo;- or &ldquo;file URI&rdquo; fully replaces the current scene and assumes <code>pos=0,0,0&amp;rot=0,0,0</code> by default (unless specified)</p></li>
<li><p>relocation/reorientation should happen locally for local URI&rsquo;s (<code>#pos=....</code>)</p></li>
@ -1085,16 +1083,20 @@ here are some hashtagbibs followed by bibtex:
<p>when an XR browser updates the human text, a quick scan for nonmatching tags (<code>@book{nonmatchingbook</code> e.g.) should be performed and prompt the enduser for deleting them.</p>
</blockquote>
<h1 id="broken-links">Broken links</h1>
<h1 id="transclusion-broken-link-resolution">Transclusion (broken link) resolution</h1>
<p>There&rsquo;s a soft-mechanism to harden links &amp; prevent broken links in various ways:</p>
<p>In spirit of Ted Nelson&rsquo;s &lsquo;transclusion resolution&rsquo;, there&rsquo;s a soft-mechanism to harden links &amp; minimize broken links in various ways:</p>
<ol>
<li>defining a different transport protocol (https vs ipfs or DAT) in <code>src</code> or <code>href</code> values can make a difference</li>
<li>mirroring files on another protocol using errorcodes in <code>src</code> or <code>href</code> properties</li>
<li>mirroring files on another protocol using (HTTP) errorcode tags in <code>src</code> or <code>href</code> properties</li>
<li>in case of <code>src</code>: nesting a copy of the embedded object in the placeholder object (<code>embeddedObject</code>) will not be replaced when the request fails</li>
</ol>
<blockquote>
<p>due to the popularity, maturity and extensiveness of HTTP codes for client/server communication, non-HTTP protocols easily map to HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)</p>
</blockquote>
<p>For example:</p>
<pre><code> +────────────────────────────────────────────────────────+
@ -1105,19 +1107,45 @@ here are some hashtagbibs followed by bibtex:
│ │ │
│ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │
│ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │
│ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
│ └ src!404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src!400: https://archive.org/l2kj43.gltf │ will be displayed.
│ └ src@404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │
+────────────────────────────────────────────────────────+
</code></pre>
<h1 id="topic-based-index-less-webrings">Topic-based index-less Webrings</h1>
<p>As hashtags in URLs map to the XWRG, <code>href</code>-values can be used to promote topic-based index-less webrings.<br>
Consider 3D scenes linking to eachother using these <code>href</code> values:</p>
<ul>
<li><code>href: schoolA.edu/projects.gltf#math</code></li>
<li><code>href: schoolB.edu/projects.gltf#math</code></li>
<li><code>href: university.edu/projects.gltf#math</code></li>
</ul>
<p>These links would all show visible links to math-tagged objects in the scene.<br>
To filter out non-related objects one could take it a step further using queries:</p>
<ul>
<li><code>href: schoolA.edu/projects.gltf#math&amp;q=-topics math</code></li>
<li><code>href: schoolB.edu/projects.gltf#math&amp;q=-courses math</code></li>
<li><code>href: university.edu/projects.gltf#math&amp;q=-theme math</code></li>
</ul>
<blockquote>
<p>This would hide all object tagged with <code>topic</code>, <code>courses</code> or <code>theme</code> (including math) so that later only objects tagged with <code>math</code> will be visible</p>
</blockquote>
<p>This makes spatial content multi-purpose, without the need to separate content into separate files, or show/hide things using a complex logiclayer like javascript.</p>
<h1 id="security-considerations">Security Considerations</h1>
<p>Since XR Text contains metadata too, the user should be able to set up tagging-rules, so the copy-paste feature can :</p>
@ -1133,8 +1161,11 @@ here are some hashtagbibs followed by bibtex:
<hr>
<p><strong>Q:</strong> Why isn&rsquo;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&rsquo;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>
<p><strong>Q:</strong> Why isn&rsquo;t there support for scripting, while we have things like WASM
<strong>A:</strong> This is out of scope as it unhyperifies hypermedia, and this is up to XR hypermedia browser-extensions.<br> Historically scripting/Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents).<br>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&rsquo;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).<br>
XR Fragments supports filtering objects in a scene only, because in the history of the javascript-powered web, showing/hiding document-entities seems to be one of the most popular basic usecases.<br>
Doing advanced scripting &amp; networkrequests under the hood are obviously interesting endavours, but this is something which should not be hardcoupled with hypermedia.<br>This belongs to browser extensions.<br>
Non-HTML Hypermedia browsers should make browser extensions the right place, to &lsquo;extend&rsquo; experiences, in contrast to code/javascript inside hypermedia documents (this turned out as a hypermedia antipattern).</p>
<h1 id="iana-considerations">IANA Considerations</h1>

View File

@ -199,17 +199,19 @@ sub-delims = "," / "="
| 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 |
| `src` | string | `"src": "#cube"` | XR embed / teleport | custom property in 3D fileformats |
| `tag` | string | `"tag": "cubes geo"` | tag object (for query-use / XRWG highlighting) | custom property in 3D fileformats |
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.
> NOTE: XR Fragments are optional but also file- and protocol-agnostic, which means that programmatic 3D scene(nodes) can also use the mechanism/metadata.
# Spatial Referencing 3D
XR Fragments assume the following objectname-to-URIFragment mapping:
```
my.io/scene.fbx
@ -229,6 +231,8 @@ Supported popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `
```
> Every 3D fileformat supports named 3D object, and this name allows URLs (fragments) to reference them (and their children objects).
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:

View File

@ -3,7 +3,7 @@
Internet Engineering Task Force L.R. van Kammen
Internet-Draft 22 September 2023
Internet-Draft 12 October 2023
Intended status: Informational
@ -13,9 +13,10 @@ Intended status: Informational
Abstract
This draft is a specification for 4D URLs & navigation, which links
together space, time & text together, for hypermedia browsers with-
or without a network-connection.
This draft is a specification for 4D URLs & hypermediatic
(https://github.com/coderofsalvation/hypermediatic) navigation, which
links together space, time & text together, for hypermedia browsers
with- or without a network-connection.
The specification promotes spatial addressibility, sharing,
navigation, query-ing and annotating interactive (text)objects across
for (XR) Browsers.
@ -41,7 +42,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 25 March 2024.
This Internet-Draft will expire on 14 April 2024.
Copyright Notice
@ -52,10 +53,9 @@ Copyright Notice
van Kammen Expires 25 March 2024 [Page 1]
van Kammen Expires 14 April 2024 [Page 1]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
This document is subject to BCP 78 and the IETF Trust's Legal
@ -79,7 +79,7 @@ Table of Contents
7. Navigating 3D . . . . . . . . . . . . . . . . . . . . . . . . 8
8. Top-level URL processing . . . . . . . . . . . . . . . . . . 9
9. Embedding XR content (src-instancing) . . . . . . . . . . . . 9
10. Navigating content (href portals) . . . . . . . . . . . . . . 12
10. Navigating content (internal/outbound href portals) . . . . . 12
10.1. UX spec . . . . . . . . . . . . . . . . . . . . . . . . 13
10.2. Scaling instanced content . . . . . . . . . . . . . . . 13
11. XR Fragment queries . . . . . . . . . . . . . . . . . . . . . 14
@ -90,12 +90,13 @@ Table of Contents
13.1. Default Data URI mimetype . . . . . . . . . . . . . . . 20
13.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 21
13.3. XR Text example parser . . . . . . . . . . . . . . . . . 22
14. Broken links . . . . . . . . . . . . . . . . . . . . . . . . 24
15. Security Considerations . . . . . . . . . . . . . . . . . . . 25
16. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
17. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 26
18. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 26
19. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 26
14. Transclusion (broken link) resolution . . . . . . . . . . . . 24
15. Topic-based index-less Webrings . . . . . . . . . . . . . . . 25
16. Security Considerations . . . . . . . . . . . . . . . . . . . 26
17. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
18. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 27
19. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 27
20. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 27
1. Introduction
@ -103,20 +104,23 @@ Table of Contents
introducing new dataformats?
Historically, there's many attempts to create the ultimate
markuplanguage or 3D fileformat.
Their lowest common denominator is: (co)authoring using plain text.
The lowest common denominator is: describing/tagging/naming nodes
using *plain text*.
van Kammen Expires 14 April 2024 [Page 2]
Internet-Draft XR Fragments October 2023
XR Fragments allows us to enrich/connect existing dataformats, by
introducing existing technologies/ideas:
van Kammen Expires 25 March 2024 [Page 2]
Internet-Draft XR Fragments September 2023
1. addressibility and navigation of 3D scenes/objects: URI Fragments
(https://en.wikipedia.org/wiki/URI_fragment) + src/href spatial
metadata
1. addressibility and hypermediatic
(https://github.com/coderofsalvation/hypermediatic) navigation of
3D scenes/objects: URI Fragments (https://en.wikipedia.org/wiki/
URI_fragment) + src/href spatial metadata
2. 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) /
@ -161,13 +165,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 3]
van Kammen Expires 14 April 2024 [Page 3]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
+===========+=============================+====================+
@ -221,9 +221,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 4]
van Kammen Expires 14 April 2024 [Page 4]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
+──────────────────────────────────────────────────────────────────────────────────────────────+
@ -246,15 +246,18 @@ Internet-Draft XR Fragments September 2023
Traditional webbrowsers can become 4D document-ready by:
* loading 3D assets (gltf/fbx e.g.) natively (with or without using
* hypermediatic (https://github.com/coderofsalvation/hypermediatic)
loading 3D assets (gltf/fbx e.g.) natively (with or without using
HTML).
* allowing assets to publish hashtags to themselves (the scene)
using the hashbus (like hashtags controlling the scrollbar).
* collapsing the 3D scene to an wordgraph (for essential navigation
purposes) controllable thru a hash(tag)bus
XR Fragments itself are HTML-agnostic, though pseudo-XR Fragment
browsers *can* be implemented on top of HTML/Javascript.
XR Fragments itself are hypermediatic
(https://github.com/coderofsalvation/hypermediatic) and HTML-
agnostic, though pseudo-XR Fragment browsers *can* be implemented on
top of HTML/Javascript.
3. Conventions and Definitions
@ -274,12 +277,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 5]
van Kammen Expires 14 April 2024 [Page 5]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
+=============================+=================================+
@ -296,65 +296,67 @@ Internet-Draft XR Fragments September 2023
4. List of URI Fragments
+==========+=========+==============+==============================+
| fragment | type | example | info |
+==========+=========+==============+==============================+
| #pos | vector3 | #pos=0.5,0,0 | positions camera (or XR |
| | | | floor) to xyz-coord 0.5,0,0, |
+----------+---------+--------------+------------------------------+
| #rot | vector3 | #rot=0,90,0 | rotates camera to xyz-coord |
| | | | 0.5,0,0 |
+----------+---------+--------------+------------------------------+
| #t | vector2 | #t=500,1000 | sets animation-loop range |
| | | | between frame 500 and 1000 |
+----------+---------+--------------+------------------------------+
| #...... | string | #.cubes | predefined views, XRWG |
| | | #cube | fragments and ID fragments |
+----------+---------+--------------+------------------------------+
+==========+=========+===============+==============================+
| fragment | type | example | info |
+==========+=========+===============+==============================+
| #pos | vector3 | #pos=0.5,0,0 | positions camera (or XR |
| | | | floor) to xyz-coord |
| | | | 0.5,0,0, |
+----------+---------+---------------+------------------------------+
| #rot | vector3 | #rot=0,90,0 | rotates camera to xyz- |
| | | | coord 0.5,0,0 |
+----------+---------+---------------+------------------------------+
| #t | vector3 | #t=1,500,1000 | play animation-loop range |
| | | | between frame 500 and |
| | | | 1000, at normal speed |
+----------+---------+---------------+------------------------------+
| #...... | string | #.cubes #cube | predefined views, XRWG |
| | | | fragments and ID |
| | | | fragments |
+----------+---------+---------------+------------------------------+
Table 3
Table 3
| xyz coordinates are similar to ones found in SVG Media Fragments
5. List of metadata for 3D nodes
+======+========+=============+===========+=========================+
| key | type | example | function | existing compatibility |
| | | (JSON) | | |
+======+========+=============+===========+=========================+
| name | string | "name": | identify/ | object supported in all |
| | | "cube" | tag | 3D fileformats & scenes |
+------+--------+-------------+-----------+-------------------------+
| tag | string | "tag": | tag | custom property in 3D |
| | | "cubes | object | fileformats |
| | | geo" | | |
+------+--------+-------------+-----------+-------------------------+
| href | string | "href": | XR | custom property in 3D |
+======+========+==========+==================+===================+
| key | type | example | function | existing |
| | | (JSON) | | compatibility |
+======+========+==========+==================+===================+
| href | string | "href": | XR teleport | custom property |
| | | "b.gltf" | | in 3D fileformats |
+------+--------+----------+------------------+-------------------+
| src | string | "src": | XR embed / | custom property |
| | | "#cube" | teleport | in 3D fileformats |
van Kammen Expires 25 March 2024 [Page 6]
van Kammen Expires 14 April 2024 [Page 6]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
| | | "b.gltf" | teleport | fileformats |
+------+--------+-------------+-----------+-------------------------+
| src | string | "src": | XR embed | custom property in 3D |
| | | "#cube" | / | fileformats |
| | | | teleport | |
+------+--------+-------------+-----------+-------------------------+
+------+--------+----------+------------------+-------------------+
| tag | string | "tag": | tag object (for | custom property |
| | | "cubes | query-use / XRWG | in 3D fileformats |
| | | geo" | highlighting) | |
+------+--------+----------+------------------+-------------------+
Table 4
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.
| NOTE: XR Fragments are optional but also file- and protocol-
| agnostic, which means that programmatic 3D scene(nodes) can also
| use the mechanism/metadata.
6. Spatial Referencing 3D
XR Fragments assume the following objectname-to-URIFragment mapping:
my.io/scene.fbx
+─────────────────────────────+
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor)
@ -370,6 +372,9 @@ Internet-Draft XR Fragments September 2023
│ +─────────────────────────+ │
+─────────────────────────────+
| Every 3D fileformat supports named 3D object, and this name allows
| URLs (fragments) to reference them (and their children objects).
Clever nested design of 3D scenes allow great ways for re-using
content, and/or previewing scenes.
For example, to render a portal with a preview-version of the scene,
@ -378,21 +383,22 @@ Internet-Draft XR Fragments September 2023
* href: https://scene.fbx
* src: https://otherworld.gltf#mainobject
van Kammen Expires 14 April 2024 [Page 7]
Internet-Draft XR Fragments October 2023
| 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.
van Kammen Expires 25 March 2024 [Page 7]
Internet-Draft XR Fragments September 2023
| embedded object. Basically an embedded link becoming an outbound
| link by activating it.
7. Navigating 3D
@ -401,7 +407,8 @@ Internet-Draft XR Fragments September 2023
+====================+=========+==================================+
| <b>#pos</b>=0,0,0 | vector3 | (re)position camera |
+--------------------+---------+----------------------------------+
| <b>#t</b>=0,100 | vector2 | (re)position looprange of scene- |
| <b>#t</b>=0,100 | vector3 | set playback speed, and |
| | | (re)position looprange of scene- |
| | | animation or src-mediacontent |
+--------------------+---------+----------------------------------+
| <b>#rot</b>=0,90,0 | vector3 | rotate camera |
@ -421,15 +428,28 @@ Internet-Draft XR Fragments September 2023
2. set the position of the camera accordingly to the vector3 values
of #pos
3. rot sets the rotation of the camera (only for non-VR/AR headsets)
4. 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)
4. t sets the playbackspeed and animation-range of the current scene
animation(s) or src-mediacontent (video/audioframes e.g., use
t=0,7,7 to 'STOP' at frame 7 e.g.)
5. 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 &#9723; and their metadata:
van Kammen Expires 14 April 2024 [Page 8]
Internet-Draft XR Fragments October 2023
+────────────────────────────────────────────────────────+
│ │
│ index.gltf │
@ -442,14 +462,6 @@ Internet-Draft XR Fragments September 2023
│ │
+────────────────────────────────────────────────────────+
van Kammen Expires 25 March 2024 [Page 8]
Internet-Draft XR Fragments September 2023
An XR Fragment-compatible browser viewing this scene, allows the end-
user to interact with the buttonA and buttonB.
In case of buttonA the end-user will be teleported to another
@ -489,21 +501,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 9]
van Kammen Expires 14 April 2024 [Page 9]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
+========+==========+==============================================+
@ -557,9 +557,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 10]
van Kammen Expires 14 April 2024 [Page 10]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
| Instead of cherrypicking objects with #bass&tuna thru src, queries
@ -613,9 +613,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 11]
van Kammen Expires 14 April 2024 [Page 11]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
&#187; example implementation
@ -627,7 +627,7 @@ Internet-Draft XR Fragments September 2023
&#187; discussion (https://github.com/coderofsalvation/xrfragment/
issues/4)
10. Navigating content (href portals)
10. Navigating content (internal/outbound href portals)
navigation, portals & mutations
@ -641,9 +641,9 @@ Internet-Draft XR Fragments September 2023
Table 7
1. clicking an ''external''- or ''file URI'' fully replaces the
current scene and assumes pos=0,0,0&rot=0,0,0 by default (unless
specified)
1. clicking an outbound ''external''- or ''file URI'' fully replaces
the current scene and assumes pos=0,0,0&rot=0,0,0 by default
(unless specified)
2. relocation/reorientation should happen locally for local URI's
(#pos=....)
@ -669,9 +669,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 12]
van Kammen Expires 14 April 2024 [Page 12]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
6. in case of navigating to a new [[pos)ition, ''first'' navigate to
@ -725,9 +725,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 13]
van Kammen Expires 14 April 2024 [Page 13]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
| REASON: non-empty placeholder object can act as a protective
@ -781,9 +781,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 14]
van Kammen Expires 14 April 2024 [Page 14]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
11.1. including/excluding
@ -837,9 +837,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 15]
van Kammen Expires 14 April 2024 [Page 15]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
11. and we set root to true or false (true=/ root selector is
@ -893,9 +893,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 16]
van Kammen Expires 14 April 2024 [Page 16]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
in games outside the browser? Through the lens of constructive lazy
@ -949,9 +949,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 17]
van Kammen Expires 14 April 2024 [Page 17]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
http://y.io/z.fbx | Derived XRWG (expressed as BibTex)
@ -1005,9 +1005,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 18]
van Kammen Expires 14 April 2024 [Page 18]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
As seen above, the XRWG can expand bibs
@ -1061,9 +1061,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 19]
van Kammen Expires 14 April 2024 [Page 19]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
12. Default font (unless specified otherwise) is a modern monospace
@ -1117,9 +1117,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 20]
van Kammen Expires 14 April 2024 [Page 20]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
* lines beginning with @ will not be rendered verbatim by default
@ -1173,9 +1173,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 21]
van Kammen Expires 14 April 2024 [Page 21]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
+--------------------------------------------------------------+ +------------------------+
@ -1229,9 +1229,9 @@ xrtext = {
van Kammen Expires 25 March 2024 [Page 22]
van Kammen Expires 14 April 2024 [Page 22]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
// bibtex: ↓@ ↓<tag|tag{phrase,|{ruler}> ↓property ↓end
@ -1285,9 +1285,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 23]
van Kammen Expires 14 April 2024 [Page 23]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
str = `
@ -1330,10 +1330,10 @@ console.log( xrtext.encode(text,tags) ) // multiplex text & bibtex back to
| nonmatching tags (@book{nonmatchingbook e.g.) should be performed
| and prompt the enduser for deleting them.
14. Broken links
14. Transclusion (broken link) resolution
There's a soft-mechanism to harden links & prevent broken links in
various ways:
In spirit of Ted Nelson's 'transclusion resolution', there's a soft-
mechanism to harden links & minimize broken links in various ways:
1. defining a different transport protocol (https vs ipfs or DAT) in
src or href values can make a difference
@ -1341,17 +1341,21 @@ console.log( xrtext.encode(text,tags) ) // multiplex text & bibtex back to
van Kammen Expires 25 March 2024 [Page 24]
van Kammen Expires 14 April 2024 [Page 24]
Internet-Draft XR Fragments September 2023
Internet-Draft XR Fragments October 2023
2. mirroring files on another protocol using errorcodes in src or
href properties
2. mirroring files on another protocol using (HTTP) errorcode tags
in src or href properties
3. in case of src: nesting a copy of the embedded object in the
placeholder object (embeddedObject) will not be replaced when the
request fails
| due to the popularity, maturity and extensiveness of HTTP codes
| for client/server communication, non-HTTP protocols easily map to
| HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)
For example:
+────────────────────────────────────────────────────────+
@ -1362,18 +1366,53 @@ Internet-Draft XR Fragments September 2023
│ │ │
│ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │
│ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │
│ │ │
│ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
│ └ src!404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src!400: https://archive.org/l2kj43.gltf │ will be displayed.
│ └ src@404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │
+────────────────────────────────────────────────────────+
15. Security Considerations
15. Topic-based index-less Webrings
As hashtags in URLs map to the XWRG, href-values can be used to
promote topic-based index-less webrings.
Consider 3D scenes linking to eachother using these href values:
* href: schoolA.edu/projects.gltf#math
* href: schoolB.edu/projects.gltf#math
* href: university.edu/projects.gltf#math
These links would all show visible links to math-tagged objects in
the scene.
To filter out non-related objects one could take it a step further
using queries:
* href: schoolA.edu/projects.gltf#math&q=-topics math
* href: schoolB.edu/projects.gltf#math&q=-courses math
van Kammen Expires 14 April 2024 [Page 25]
Internet-Draft XR Fragments October 2023
* href: university.edu/projects.gltf#math&q=-theme math
| This would hide all object tagged with topic, courses or theme
| (including math) so that later only objects tagged with math will
| be visible
This makes spatial content multi-purpose, without the need to
separate content into separate files, or show/hide things using a
complex logiclayer like javascript.
16. Security Considerations
Since XR Text contains metadata too, the user should be able to set
up tagging-rules, so the copy-paste feature can :
@ -1381,7 +1420,7 @@ Internet-Draft XR Fragments September 2023
* filter out sensitive data when copy/pasting (XR text with
tag:secret e.g.)
16. FAQ
17. FAQ
*Q:* Why is everything HTTP GET-based, what about POST/PUT/DELETE
HATEOS
@ -1390,38 +1429,50 @@ Internet-Draft XR Fragments September 2023
(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
van Kammen Expires 25 March 2024 [Page 25]
Internet-Draft XR Fragments September 2023
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
*Q:* Why isn't there support for scripting, while we have things like
WASM *A:* This is out of scope as it unhyperifies hypermedia, and
this is up to XR hypermedia browser-extensions.
Historically scripting/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).
XR Fragments supports filtering objects in a scene only, because in
the history of the javascript-powered web, showing/hiding document-
entities seems to be one of the most popular basic usecases.
Doing advanced scripting & networkrequests under the hood are
obviously interesting endavours, but this is something which should
not be hardcoupled with hypermedia.
This belongs to browser extensions.
17. IANA Considerations
van Kammen Expires 14 April 2024 [Page 26]
Internet-Draft XR Fragments October 2023
Non-HTML Hypermedia browsers should make browser extensions the right
place, to 'extend' experiences, in contrast to code/javascript inside
hypermedia documents (this turned out as a hypermedia antipattern).
18. IANA Considerations
This document has no IANA actions.
18. Acknowledgments
19. Acknowledgments
* NLNET (https://nlnet.nl)
* Future of Text (https://futureoftext.org)
* visual-meta.info (https://visual-meta.info)
19. Appendix: Definitions
20. Appendix: Definitions
+=================+===============================================+
| definition | explanation |
@ -1450,19 +1501,19 @@ Internet-Draft XR Fragments September 2023
| spacetime | positions camera, triggers scene-preset/time |
| hashtags | |
+-----------------+-----------------------------------------------+
van Kammen Expires 25 March 2024 [Page 26]
Internet-Draft XR Fragments September 2023
| 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. |
van Kammen Expires 14 April 2024 [Page 27]
Internet-Draft XR Fragments October 2023
+-----------------+-----------------------------------------------+
| placeholder | a 3D object which with src-metadata (which |
| object | will be replaced by the src-data.) |
@ -1506,14 +1557,6 @@ Internet-Draft XR Fragments September 2023
| BibTag | a BibTeX tag |
+-----------------+-----------------------------------------------+
| (hashtag)bibs | an easy to speak/type/scan tagging SDL (see |
van Kammen Expires 25 March 2024 [Page 27]
Internet-Draft XR Fragments September 2023
| | here (https://github.com/coderofsalvation/ |
| | hashtagbibs) which expands to BibTex/JSON/XML |
+-----------------+-----------------------------------------------+
@ -1522,47 +1565,4 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 25 March 2024 [Page 28]
van Kammen Expires 14 April 2024 [Page 28]

View File

@ -10,7 +10,7 @@
<workgroup>Internet Engineering Task Force</workgroup>
<abstract>
<t>This draft is a specification for 4D URLs &amp; navigation, which links together space, time &amp; text together, for hypermedia browsers with- or without a network-connection.<br />
<t>This draft is a specification for 4D URLs &amp; <eref target="https://github.com/coderofsalvation/hypermediatic">hypermediatic</eref> navigation, which links together space, time &amp; text together, for hypermedia browsers with- or without a network-connection.<br />
The specification promotes spatial addressibility, sharing, navigation, query-ing and annotating interactive (text)objects across for (XR) Browsers.<br />
@ -28,13 +28,13 @@ XR Fragments allows us to enrich existing dataformats, by recursive use of exist
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 />
The lowest common denominator is: describing/tagging/naming nodes using <strong>plain text</strong>.<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>addressibility and <eref target="https://github.com/coderofsalvation/hypermediatic">hypermediatic</eref> 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/&amp; 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>
@ -132,11 +132,11 @@ But approaches things from a higherlevel feedbackloop/hypermedia browser-perspec
<t>Traditional webbrowsers can become 4D document-ready by:</t>
<ul spacing="compact">
<li>loading 3D assets (gltf/fbx e.g.) natively (with or without using HTML).</li>
<li><eref target="https://github.com/coderofsalvation/hypermediatic">hypermediatic</eref> loading 3D assets (gltf/fbx e.g.) natively (with or without using HTML).</li>
<li>allowing assets to publish hashtags to themselves (the scene) using the hashbus (like hashtags controlling the scrollbar).</li>
<li>collapsing the 3D scene to an wordgraph (for essential navigation purposes) controllable thru a hash(tag)bus</li>
</ul>
<t>XR Fragments itself are HTML-agnostic, though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript.</t>
<t>XR Fragments itself are <eref target="https://github.com/coderofsalvation/hypermediatic">hypermediatic</eref> and HTML-agnostic, though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript.</t>
</section>
<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
@ -200,9 +200,9 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<tr>
<td><tt>#t</tt></td>
<td>vector2</td>
<td><tt>#t=500,1000</tt></td>
<td>sets animation-loop range between frame 500 and 1000</td>
<td>vector3</td>
<td><tt>#t=1,500,1000</tt></td>
<td>play animation-loop range between frame 500 and 1000, at normal speed</td>
</tr>
<tr>
@ -228,22 +228,6 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</thead>
<tbody>
<tr>
<td><tt>name</tt></td>
<td>string</td>
<td><tt>&quot;name&quot;: &quot;cube&quot;</tt></td>
<td>identify/tag</td>
<td>object supported in all 3D fileformats &amp; scenes</td>
</tr>
<tr>
<td><tt>tag</tt></td>
<td>string</td>
<td><tt>&quot;tag&quot;: &quot;cubes geo&quot;</tt></td>
<td>tag object</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><tt>href</tt></td>
<td>string</td>
@ -259,12 +243,21 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<td>XR embed / teleport</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><tt>tag</tt></td>
<td>string</td>
<td><tt>&quot;tag&quot;: &quot;cubes geo&quot;</tt></td>
<td>tag object (for query-use / XRWG highlighting)</td>
<td>custom property in 3D fileformats</td>
</tr>
</tbody>
</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><t>NOTE: XR Fragments are optional but also file- and protocol-agnostic, which means that programmatic 3D scene(nodes) can also use the mechanism/metadata.</t>
</blockquote></section>
<section anchor="spatial-referencing-3d"><name>Spatial Referencing 3D</name>
<t>XR Fragments assume the following objectname-to-URIFragment mapping:</t>
<artwork>
my.io/scene.fbx
@ -283,7 +276,8 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
+─────────────────────────────+
</artwork>
<t>Clever nested design of 3D scenes allow great ways for re-using content, and/or previewing scenes.<br />
<blockquote><t>Every 3D fileformat supports named 3D object, and this name allows URLs (fragments) to reference them (and their children objects).</t>
</blockquote><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>
@ -291,7 +285,7 @@ For example, to render a portal with a preview-version of the scene, create an 3
<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><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. Basically an embedded link becoming an outbound link by activating it.</t>
</blockquote></section>
<section anchor="navigating-3d"><name>Navigating 3D</name>
@ -313,8 +307,8 @@ For example, to render a portal with a preview-version of the scene, create an 3
<tr>
<td>&lt;b&gt;#t&lt;/b&gt;=0,100</td>
<td>vector2</td>
<td>(re)position looprange of scene-animation or <tt>src</tt>-mediacontent</td>
<td>vector3</td>
<td>set playback speed, and (re)position looprange of scene-animation or <tt>src</tt>-mediacontent</td>
</tr>
<tr>
@ -332,7 +326,7 @@ For example, to render a portal with a preview-version of the scene, create an 3
<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><tt>t</tt> sets the playbackspeed and animation-range of the current scene animation(s) or <tt>src</tt>-mediacontent (video/audioframes e.g., use <tt>t=0,7,7</tt> to 'STOP' at frame 7 e.g.)</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>
@ -450,7 +444,7 @@ Resizing will be happen accordingly to its placeholder object <tt>aquariumcube</
</t>
</section>
<section anchor="navigating-content-href-portals"><name>Navigating content (href portals)</name>
<section anchor="navigating-content-internal-outbound-href-portals"><name>Navigating content (internal/outbound href portals)</name>
<t>navigation, portals &amp; mutations</t>
<table>
<thead>
@ -473,7 +467,7 @@ Resizing will be happen accordingly to its placeholder object <tt>aquariumcube</
</tbody>
</table>
<ol>
<li><t>clicking an ''external''- or ''file URI'' fully replaces the current scene and assumes <tt>pos=0,0,0&amp;rot=0,0,0</tt> by default (unless specified)</t>
<li><t>clicking an outbound ''external''- or ''file URI'' fully replaces the current scene and assumes <tt>pos=0,0,0&amp;rot=0,0,0</tt> by default (unless specified)</t>
</li>
<li><t>relocation/reorientation should happen locally for local URI's (<tt>#pos=....</tt>)</t>
</li>
@ -943,15 +937,16 @@ here are some hashtagbibs followed by bibtex:
</blockquote></section>
</section>
<section anchor="broken-links"><name>Broken links</name>
<t>There's a soft-mechanism to harden links &amp; prevent broken links in various ways:</t>
<section anchor="transclusion-broken-link-resolution"><name>Transclusion (broken link) resolution</name>
<t>In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links &amp; minimize broken links in various ways:</t>
<ol spacing="compact">
<li>defining a different transport protocol (https vs ipfs or DAT) in <tt>src</tt> or <tt>href</tt> values can make a difference</li>
<li>mirroring files on another protocol using errorcodes in <tt>src</tt> or <tt>href</tt> properties</li>
<li>mirroring files on another protocol using (HTTP) errorcode tags in <tt>src</tt> or <tt>href</tt> properties</li>
<li>in case of <tt>src</tt>: nesting a copy of the embedded object in the placeholder object (<tt>embeddedObject</tt>) will not be replaced when the request fails</li>
</ol>
<t>For example:</t>
<blockquote><t>due to the popularity, maturity and extensiveness of HTTP codes for client/server communication, non-HTTP protocols easily map to HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)</t>
</blockquote><t>For example:</t>
<artwork> +────────────────────────────────────────────────────────+
│ │
@ -961,20 +956,43 @@ here are some hashtagbibs followed by bibtex:
│ │ │
│ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │
│ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │
│ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
│ └ src!404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src!400: https://archive.org/l2kj43.gltf │ will be displayed.
│ └ src@404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │
+────────────────────────────────────────────────────────+
</artwork>
</section>
<section anchor="topic-based-index-less-webrings"><name>Topic-based index-less Webrings</name>
<t>As hashtags in URLs map to the XWRG, <tt>href</tt>-values can be used to promote topic-based index-less webrings.<br />
Consider 3D scenes linking to eachother using these <tt>href</tt> values:</t>
<ul spacing="compact">
<li><tt>href: schoolA.edu/projects.gltf#math</tt></li>
<li><tt>href: schoolB.edu/projects.gltf#math</tt></li>
<li><tt>href: university.edu/projects.gltf#math</tt></li>
</ul>
<t>These links would all show visible links to math-tagged objects in the scene.<br />
To filter out non-related objects one could take it a step further using queries:</t>
<ul spacing="compact">
<li><tt>href: schoolA.edu/projects.gltf#math&amp;q=-topics math</tt></li>
<li><tt>href: schoolB.edu/projects.gltf#math&amp;q=-courses math</tt></li>
<li><tt>href: university.edu/projects.gltf#math&amp;q=-theme math</tt></li>
</ul>
<blockquote><t>This would hide all object tagged with <tt>topic</tt>, <tt>courses</tt> or <tt>theme</tt> (including math) so that later only objects tagged with <tt>math</tt> will be visible</t>
</blockquote><t>This makes spatial content multi-purpose, without the need to separate content into separate files, or show/hide things using a complex logiclayer like javascript.</t>
</section>
<section anchor="security-considerations"><name>Security Considerations</name>
<t>Since XR Text contains metadata too, the user should be able to set up tagging-rules, so the copy-paste feature can :</t>
@ -987,8 +1005,17 @@ here are some hashtagbibs followed by bibtex:
<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>
<t><strong>Q:</strong> Why isn't there support for scripting, while we have things like WASM
<strong>A:</strong> This is out of scope as it unhyperifies hypermedia, and this is up to XR hypermedia browser-extensions.<br />
Historically scripting/Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents).<br />
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).<br />
XR Fragments supports filtering objects in a scene only, because in the history of the javascript-powered web, showing/hiding document-entities seems to be one of the most popular basic usecases.<br />
Doing advanced scripting &amp; networkrequests under the hood are obviously interesting endavours, but this is something which should not be hardcoupled with hypermedia.<br />
This belongs to browser extensions.<br />
Non-HTML Hypermedia browsers should make browser extensions the right place, to 'extend' experiences, in contrast to code/javascript inside hypermedia documents (this turned out as a hypermedia antipattern).</t>
</section>
<section anchor="iana-considerations"><name>IANA Considerations</name>

View File

@ -129,6 +129,52 @@ Macros enrich existing spatial content with a lowcode, limited logic-layer, by r
This is done by allowing string/integer variables, and the <code>|</code> symbol to roundrobin variable values.<br>
Macros also act as events, so more serious scripting languages can react to them as well.<br></p>
<table>
<thead>
<tr>
<th>key</th>
<th>type</th>
<th>example (JSON)</th>
<th>function</th>
<th>existing compatibility</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@bg</code></td>
<td>string</td>
<td><code>&quot;@bg&quot;:&quot;#cube&quot;</code></td>
<td>bg: binds fog near/far based to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><code>@fog</code></td>
<td>string</td>
<td><code>&quot;@fog&quot;:&quot;#cube&quot;</code></td>
<td>fog: binds fog near/far based to cube x/y (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><code>@scroll</code></td>
<td>string</td>
<td><code>&quot;@scroll&quot;:&quot;#cube&quot;</code></td>
<td>texturescrolling: binds texture x/y/rot based to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><code>@emissive</code></td>
<td>string</td>
<td><code>&quot;@emissive&quot;:&quot;#cube&quot;</code></td>
<td>day/night/mood: binds material&rsquo;s emissive value to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
</tbody>
</table>
<h2 id="usecase-click-object">Usecase: click object</h2>
<table>

View File

@ -134,6 +134,13 @@ Macros enrich existing spatial content with a lowcode, limited logic-layer, by r
This is done by allowing string/integer variables, and the `|` symbol to roundrobin variable values.<br>
Macros also act as events, so more serious scripting languages can react to them as well.<br>
| key | type | example (JSON) | function | existing compatibility |
|--------------|----------|------------------------|---------------------|----------------------------------------|
| `@bg` | string | `"@bg":"#cube"` | bg: binds fog near/far based to cube x/y/z (anim) values | custom property in 3D fileformats |
| `@fog` | string | `"@fog":"#cube"` | fog: binds fog near/far based to cube x/y (anim) values | custom property in 3D fileformats |
| `@scroll` | string | `"@scroll":"#cube"` | texturescrolling: binds texture x/y/rot based to cube x/y/z (anim) values | custom property in 3D fileformats |
| `@emissive` | string | `"@emissive":"#cube"` | day/night/mood: binds material's emissive value to cube x/y/z (anim) values | custom property in 3D fileformats |
## Usecase: click object
| custom property | value | trigger when |

View File

@ -3,7 +3,7 @@
Internet Engineering Task Force L.R. van Kammen
Internet-Draft 22 September 2023
Internet-Draft 12 October 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 25 March 2024.
This Internet-Draft will expire on 14 April 2024.
Copyright Notice
@ -53,9 +53,9 @@ Copyright Notice
van Kammen Expires 25 March 2024 [Page 1]
van Kammen Expires 14 April 2024 [Page 1]
Internet-Draft XR Macros September 2023
Internet-Draft XR Macros October 2023
extracted from this document must include Revised BSD License text as
@ -68,17 +68,17 @@ Table of Contents
2. Core principle . . . . . . . . . . . . . . . . . . . . . . . 2
3. Conventions and Definitions . . . . . . . . . . . . . . . . . 3
4. List of XR Macros . . . . . . . . . . . . . . . . . . . . . . 3
4.1. Usecase: click object . . . . . . . . . . . . . . . . . . 3
4.2. Usecase: conditional click object . . . . . . . . . . . . 3
4.3. Usecase: click object (roundrobin) . . . . . . . . . . . 4
4.1. Usecase: click object . . . . . . . . . . . . . . . . . . 4
4.2. Usecase: conditional click object . . . . . . . . . . . . 4
4.3. Usecase: click object (roundrobin) . . . . . . . . . . . 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
trigger . . . . . . . . . . . . . . . . . . . . . . . . . 5
4.5. Usecase: present context menu with options . . . . . . . 6
4.6. Event Bubble-flow . . . . . . . . . . . . . . . . . . . . 6
5. Security Considerations . . . . . . . . . . . . . . . . . . . 7
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 7
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 7
8. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 7
1. Introduction
@ -109,9 +109,9 @@ Table of Contents
van Kammen Expires 25 March 2024 [Page 2]
van Kammen Expires 14 April 2024 [Page 2]
Internet-Draft XR Macros September 2023
Internet-Draft XR Macros October 2023
3. Metadata-values can contain the | symbol to &#127922; roundrobin
@ -144,6 +144,61 @@ Internet-Draft XR Macros September 2023
Macros also act as events, so more serious scripting languages can
react to them as well.
van Kammen Expires 14 April 2024 [Page 3]
Internet-Draft XR Macros October 2023
+=========+======+===================+=================+=============+
|key |type |example (JSON) |function |existing |
| | | | |compatibility|
+=========+======+===================+=================+=============+
|@bg |string|"@bg":"#cube" |bg: binds fog |custom |
| | | |near/far based to|property in |
| | | |cube x/y/z (anim)|3D |
| | | |values |fileformats |
+---------+------+-------------------+-----------------+-------------+
|@fog |string|"@fog":"#cube" |fog: binds fog |custom |
| | | |near/far based to|property in |
| | | |cube x/y (anim) |3D |
| | | |values |fileformats |
+---------+------+-------------------+-----------------+-------------+
|@scroll |string|"@scroll":"#cube" |texturescrolling:|custom |
| | | |binds texture |property in |
| | | |x/y/rot based to |3D |
| | | |cube x/y/z (anim)|fileformats |
| | | |values | |
+---------+------+-------------------+-----------------+-------------+
|@emissive|string|"@emissive":"#cube"|day/night/mood: |custom |
| | | |binds material's |property in |
| | | |emissive value to|3D |
| | | |cube x/y/z (anim)|fileformats |
| | | |values | |
+---------+------+-------------------+-----------------+-------------+
Table 1
4.1. Usecase: click object
+=================+================+================+
@ -152,7 +207,7 @@ Internet-Draft XR Macros September 2023
| !clickme | bg=1,1,1&foo=2 | object clicked |
+-----------------+----------------+----------------+
Table 1
Table 2
4.2. Usecase: conditional click object
@ -162,17 +217,16 @@ 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 25 March 2024 [Page 3]
Internet-Draft XR Macros September 2023
+-----------------+------------------+----------------------------+
Table 2
van Kammen Expires 14 April 2024 [Page 4]
Internet-Draft XR Macros October 2023
Table 3
| when a user clicks an object with the custom properties above, it
| should set the backgroundcolor to 1,1,1 when foo is greater than 2
@ -192,7 +246,7 @@ Internet-Draft XR Macros September 2023
| night | bg=0,0,0&foo=2 | roundrobin |
+-----------------+----------------+----------------+
Table 3
Table 4
| when a user clicks an object with the custom properties above, it
| should trigger either day noon or night in roundrobin fashion.
@ -215,15 +269,17 @@ Internet-Draft XR Macros September 2023
| night | bg=0,0,0&foo=2 | roundrobin |
+-----------------+----------------+----------------------+
Table 4
Table 5
van Kammen Expires 25 March 2024 [Page 4]
van Kammen Expires 14 April 2024 [Page 5]
Internet-Draft XR Macros September 2023
Internet-Draft XR Macros October 2023
4.5. Usecase: present context menu with options
@ -240,7 +296,7 @@ Internet-Draft XR Macros September 2023
| !night | bg=0,0,0&foo=2 | clicked in contextmenu |
+-----------------+----------------+------------------------+
Table 5
Table 6
| When interacting with an object with more than one !-macro, the XR
| Browser should offer a contextmenu to execute a macro.
@ -277,9 +333,9 @@ click object with (`!clickme`:`!foo|!bar|!flop` e.g.)
van Kammen Expires 25 March 2024 [Page 5]
van Kammen Expires 14 April 2024 [Page 6]
Internet-Draft XR Macros September 2023
Internet-Draft XR Macros October 2023
| Note that only macro's can trigger roundrobin values or
@ -326,11 +382,11 @@ Internet-Draft XR Macros September 2023
| | a salad of machine-symbols and words |
+---------------+---------------------------------------------------+
Table 6
Table 7
van Kammen Expires 25 March 2024 [Page 6]
van Kammen Expires 14 April 2024 [Page 7]

View File

@ -63,7 +63,51 @@ This is done by allowing string/integer variables, and the <tt>|</tt> symbol to
Macros also act as events, so more serious scripting languages can react to them as well.<br />
</t>
<table>
<thead>
<tr>
<th>key</th>
<th>type</th>
<th>example (JSON)</th>
<th>function</th>
<th>existing compatibility</th>
</tr>
</thead>
<tbody>
<tr>
<td><tt>@bg</tt></td>
<td>string</td>
<td><tt>&quot;@bg&quot;:&quot;#cube&quot;</tt></td>
<td>bg: binds fog near/far based to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><tt>@fog</tt></td>
<td>string</td>
<td><tt>&quot;@fog&quot;:&quot;#cube&quot;</tt></td>
<td>fog: binds fog near/far based to cube x/y (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><tt>@scroll</tt></td>
<td>string</td>
<td><tt>&quot;@scroll&quot;:&quot;#cube&quot;</tt></td>
<td>texturescrolling: binds texture x/y/rot based to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
<tr>
<td><tt>@emissive</tt></td>
<td>string</td>
<td><tt>&quot;@emissive&quot;:&quot;#cube&quot;</tt></td>
<td>day/night/mood: binds material's emissive value to cube x/y/z (anim) values</td>
<td>custom property in 3D fileformats</td>
</tr>
</tbody>
</table>
<section anchor="usecase-click-object"><name>Usecase: click object</name>
<table>
<thead>

View File

@ -12,16 +12,19 @@
<script src="./../../../dist/xrfragment.aframe.js"></script>
</head>
<body>
<div id="overlay">
<div id="overlay" style="display:none">
<img src="./../../assets/logo.png" class="logo"/>
<button id="navback" onclick="history.back()">&lt;</button>
<button id="navforward" onclick="history.forward()">&gt;</button>
<input type="submit" value="load 3D asset"></input>
<input type="submit" value="load 3D file"></input>
<input type="text" id="uri" value="" onchange="AFRAME.XRF.navigator.to( $('#uri').value )"/>
</div>
<!-- open AFRAME inspector: $('a-scene').components.inspector.openInspector() -->
<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>
<a class="btn-foot" id="model" target="_blank" href="example.gltf">⬇️ model</a>
<a class="btn-foot" id="more" target="_blank">XRF</a>
<textarea style="display:none"></textarea>
<a-scene light="defaultLightsEnabled: false">
<a-entity id="player" wasd-controls look-controls>

View File

@ -73,7 +73,6 @@ input[type="submit"] {
}
#overlay > #uri {
display:none;
height: 29px;
font-size: 21px;
position: absolute;
@ -92,25 +91,40 @@ input[type="submit"] {
border: 5px solid #1c1c3299;
padding: 0px 6px;
position: absolute;
font-weight: bold;
font-weight: 1000;
font-family: sans-serif;
color: #7c7c7c;
font-size:17px;
color: #888;
height:43px;
z-index:2000;
cursor:pointer;
right: 10px;
text-transform:uppercase;
}
a.btn-foot#embed{
color: #888;
bottom: 126px;
bottom: 129px;
}
a.btn-foot#model{
bottom:73px;
bottom:72px;
}
a.btn-foot#more{
bottom:72px;
width: 60px;
text-align: center;
}
a.btn-foot#source{
bottom:179px;
bottom:186px;
}
a#source,
a#embed,
a#model{
display:none
}
html.a-fullscreen a.btn-foot {
@ -251,3 +265,7 @@ html{
background-color: #4443;
}
.a-enter-vr-button, .a-enter-ar-button{
height:41px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -54,17 +54,22 @@ export function setupConsole(el){
}
export function setupUrlBar(el,XRF){
var isIframe = (window === window.parent || window.opener) ? false : true;
if( isIframe || document.location.href.match(/localhost/) ){
// show internal URL bar to test XR fragments interactively
el.style.display = 'block'
XRF.addEventListener('updateHash', () => reflectUrl() )
const reflectUrl = (url) => el.value = url || document.location.search.substr(1) + document.location.hash
reflectUrl()
var isIframe = document.location.hash.match(/embed=1/)
let ids = ['#overlay','a#embed','a#source','a#model']
let showButtons = () => {
ids.map( (i) => $(i).style.display = 'block' )
$('a#more').style.display = 'none'
}
if( isIframe ){
// show internal URL bar & backbuttons to test XR fragments interactively
showButtons()
}else{
$('a#more').addEventListener('click', () => showButtons() )
}
XRF.addEventListener('updateHash', () => reflectUrl() )
const reflectUrl = (url) => el.value = url || document.location.search.substr(1) + document.location.hash
reflectUrl()
}
function SnackBar(userOptions) {

View File

@ -10,12 +10,15 @@
<body>
<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="submit" value="load 3D file"></input>
<input type="text" id="uri" value="" onchange="XRF.navigator.to( $('#uri').value )"/>
</div>
<!-- open AFRAME inspector: $('a-scene').components.inspector.openInspector() -->
<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>
<a class="btn-foot" id="model" target="_blank" href="index.gltf">⬇️ model</a>
<a class="btn-foot" id="more" target="_blank" href="index.gltf">XRF</a>
<textarea style="display:none"></textarea>
<script async src="./../../assets/js/alpine.min.js"></script>

File diff suppressed because one or more lines are too long

View File

@ -12,9 +12,9 @@ window.AFRAME.registerComponent('xrf', {
let aScene = document.querySelector('a-scene')
let XRF = AFRAME.XRF = xrf.init({
THREE,
camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
camera: aScene.camera,
scene: aScene.object3D,
renderer: aScene.renderer,
debug: true,
loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?

View File

@ -32,7 +32,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){
if( nocollide.tid ) return // ratelimit
_event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) )
scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 )
}

View File

@ -34,6 +34,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts)
.then( () => {

View File

@ -52,6 +52,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play()
})
@ -65,9 +66,10 @@ xrf.parseModel = function(model,url){
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 )) * xrf.focusLine.opacity;
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.3
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.2
if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
}
}
xrf.getLastModel = () => xrf.model.last

View File

@ -35,8 +35,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
hashbus.pub( url, model ) // and eval URI XR fragments
// spec: 2. init metadata
let frag = hashbus.pub( url, model ) // and eval URI XR fragments
// spec: predefined view(s) from URL (https://xrfragment.org/#predefined_view)
setTimeout( () => { // give external objects some slack
xrf.frag.updatePredefinedView({model,scene:model.scene,frag})
},2000)
xrf.add( model.scene )
xrf.navigator.updateHash(hash)
resolve(model)
@ -49,9 +53,14 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
})
window.addEventListener('hashchange', function (e){
xrf.emit('hash', {hash: document.location.hash })
})
// this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group()

View File

@ -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 = color; //selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
vec4 grayscale_color = selected ? color : vec4(vec3(luminance) + vec3(0.33), color.a);
gl_FragColor = grayscale_color;
}
`,
@ -109,14 +109,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed
if( mesh.material ){
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
}
xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false
if( o.material ){
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
}
})
// update mouse cursor
if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state )

View File

@ -6,7 +6,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
}
xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts
let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => {
@ -14,7 +14,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection
if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name
if( mesh.visible ){
if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag})
.then( () => {
const color = new THREE.Color();
@ -82,14 +82,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) )
// create new selections
match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
if( w.key == `#${id}` ){
if( w.value && w.value[0] == '#' ){
// if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
}
}
w.nodes.map( (mesh) => {
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
})
}
@ -99,6 +98,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src")
console.dir(frag)
}else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) {
let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -113,7 +114,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url
xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
console.dir({opts,frag})
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
})

View File

@ -27,10 +27,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries
let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => {
for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)

View File

@ -3,23 +3,41 @@
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
let { mesh, model, camera, scene, renderer, THREE, hashbus} = opts
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
console.log(" └ instancing src")
let src;
let url = v.string
let frag = xrfragment.URI.parse(url)
let vfrag = xrfragment.URI.parse(url)
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
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 )
enableSourcePortation(src)
mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false
}
const enableSourcePortation = (src) => {
if( vfrag.href || v.string[0] == '#' ) return
let scale = new THREE.Vector3()
let size = new THREE.Vector3()
mesh.getWorldScale(scale)
new THREE.Box3().setFromObject(src).getSize(size)
const geo = new THREE.SphereGeometry( Math.max(size.x, size.y, size.z) / scale.x, 10, 10 )
const mat = new THREE.MeshBasicMaterial()
mat.transparent = true
mat.roughness = 0.05
mat.metalness = 1
mat.opacity = 0
const cube = new THREE.Mesh( geo, mat )
console.log("todo: sourceportate")
//mesh.add(cube)
}
const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' })
.then( (res) => {
@ -38,8 +56,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error )
}
if( url[0] == "#" ) addScene(scene,url,frag) // current file
else externalSRC(url,frag) // external file
if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,vfrag) // external file
}
xrf.frag.src.eval = function(scene, opts, url){