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 pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k]; let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator // call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts) xrf.emit(k,opts)
.then( () => { .then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene) model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => { model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim ) anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play() 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.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.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; 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 if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
} }
} }
xrf.getLastModel = () => xrf.model.last xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){ function nocollide(){
if( nocollide.tid ) return // ratelimit if( nocollide.tid ) return // ratelimit
_event.type = "nocollide" _event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) ) scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 ) 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}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene}) xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view) // spec: 2. init metadata
hashbus.pub( url, model ) // and eval URI XR fragments 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.add( model.scene )
xrf.navigator.updateHash(hash) xrf.navigator.updateHash(hash)
resolve(model) resolve(model)
@ -1096,10 +1103,15 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) 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) // this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group() xrf.focusLine = new xrf.THREE.Group()
xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true}) xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true})
@ -1398,7 +1410,7 @@ xrf.frag.href = function(v, opts){
vec4 color = texture2D(pano, sampleUV); vec4 color = texture2D(pano, sampleUV);
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js) // 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; 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; gl_FragColor = grayscale_color;
} }
`, `,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => { let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
if( mesh.material ){ xrf.interactive.objects.map( (o) => {
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state let newState = o.name == mesh.name ? state : false
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0 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 // update mouse cursor
if( !renderer.domElement.lastCursor ) if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree .emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state ) .then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
} }
xrf.frag.updatePredefinedView = (opts) => { xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest // spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => { const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection let oldSelection
if(!id) return id // important: ignore empty strings if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name // Selection of Interest if predefined_view matches object name
if( mesh.visible ){ if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag}) xrf.emit('focus',{...opts,frag})
.then( () => { .then( () => {
const color = new THREE.Color(); const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) ) remove.map( (n) => scene.remove(n.selection) )
// create new selections // create new selections
match.map( (w) => { match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){ if( w.key == `#${id}` ){
// if value is alias, execute fragment value if( w.value && w.value[0] == '#' ){
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR ) // 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) => { w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
}) })
} }
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src") console.log("filtering predefined view of src")
console.dir(frag) console.dir(frag)
}else{ }else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) { for ( let i in frag ) {
let v = frag[i] let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){ if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url // react to enduser typing url
xrf.addEventListener('hash', (opts) => { xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) 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}) xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
}) })
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){ xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries // spec: https://xrfragment.org/#queries
let q = frag.q.query let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => { scene.traverse( (mesh) => {
for ( let i in q ) { for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId 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.) 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){ xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment 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") console.log(" └ instancing src")
let src; let src;
let url = v.string 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 opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
const addScene = (scene,url,frag) => { const addScene = (scene,url,frag) => {
src = xrf.frag.src.filterScene(scene,{...opts,frag}) src = xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( src, opts, url ) xrf.frag.src.scale( src, opts, url )
xrf.frag.src.eval( src, opts, url ) xrf.frag.src.eval( src, opts, url )
enableSourcePortation(src)
mesh.add(src) mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false 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) => { const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error ) .catch( console.error )
} }
if( url[0] == "#" ) addScene(scene,url,frag) // current file if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,frag) // external file else externalSRC(url,vfrag) // external file
} }
xrf.frag.src.eval = function(scene, opts, url){ xrf.frag.src.eval = function(scene, opts, url){
@ -1916,9 +1948,9 @@ window.AFRAME.registerComponent('xrf', {
let aScene = document.querySelector('a-scene') let aScene = document.querySelector('a-scene')
let XRF = AFRAME.XRF = xrf.init({ let XRF = AFRAME.XRF = xrf.init({
THREE, THREE,
camera: aScene.camera, camera: aScene.camera,
scene: aScene.object3D, scene: aScene.object3D,
renderer: aScene.renderer, renderer: aScene.renderer,
debug: true, debug: true,
loaders: { loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments? 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 pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k]; let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator // call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts) xrf.emit(k,opts)
.then( () => { .then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene) model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => { model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim ) anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play() 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.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.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; 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 if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
} }
} }
xrf.getLastModel = () => xrf.model.last xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){ function nocollide(){
if( nocollide.tid ) return // ratelimit if( nocollide.tid ) return // ratelimit
_event.type = "nocollide" _event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) ) scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 ) 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}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene}) xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view) // spec: 2. init metadata
hashbus.pub( url, model ) // and eval URI XR fragments 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.add( model.scene )
xrf.navigator.updateHash(hash) xrf.navigator.updateHash(hash)
resolve(model) resolve(model)
@ -1096,10 +1103,15 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) 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) // this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group() xrf.focusLine = new xrf.THREE.Group()
xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true}) xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true})
@ -1398,7 +1410,7 @@ xrf.frag.href = function(v, opts){
vec4 color = texture2D(pano, sampleUV); vec4 color = texture2D(pano, sampleUV);
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js) // 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; 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; gl_FragColor = grayscale_color;
} }
`, `,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => { let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
if( mesh.material ){ xrf.interactive.objects.map( (o) => {
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state let newState = o.name == mesh.name ? state : false
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0 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 // update mouse cursor
if( !renderer.domElement.lastCursor ) if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree .emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state ) .then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
} }
xrf.frag.updatePredefinedView = (opts) => { xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest // spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => { const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection let oldSelection
if(!id) return id // important: ignore empty strings if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name // Selection of Interest if predefined_view matches object name
if( mesh.visible ){ if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag}) xrf.emit('focus',{...opts,frag})
.then( () => { .then( () => {
const color = new THREE.Color(); const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) ) remove.map( (n) => scene.remove(n.selection) )
// create new selections // create new selections
match.map( (w) => { match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){ if( w.key == `#${id}` ){
// if value is alias, execute fragment value if( w.value && w.value[0] == '#' ){
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR ) // 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) => { w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
}) })
} }
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src") console.log("filtering predefined view of src")
console.dir(frag) console.dir(frag)
}else{ }else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) { for ( let i in frag ) {
let v = frag[i] let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){ if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url // react to enduser typing url
xrf.addEventListener('hash', (opts) => { xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) 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}) xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
}) })
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){ xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries // spec: https://xrfragment.org/#queries
let q = frag.q.query let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => { scene.traverse( (mesh) => {
for ( let i in q ) { for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId 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.) 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){ xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment 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") console.log(" └ instancing src")
let src; let src;
let url = v.string 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 opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
const addScene = (scene,url,frag) => { const addScene = (scene,url,frag) => {
src = xrf.frag.src.filterScene(scene,{...opts,frag}) src = xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( src, opts, url ) xrf.frag.src.scale( src, opts, url )
xrf.frag.src.eval( src, opts, url ) xrf.frag.src.eval( src, opts, url )
enableSourcePortation(src)
mesh.add(src) mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false 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) => { const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error ) .catch( console.error )
} }
if( url[0] == "#" ) addScene(scene,url,frag) // current file if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,frag) // external file else externalSRC(url,vfrag) // external file
} }
xrf.frag.src.eval = function(scene, opts, url){ 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 pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k]; let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator // call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts) xrf.emit(k,opts)
.then( () => { .then( () => {
@ -852,6 +853,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene) model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => { model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim ) anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play() 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.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.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; 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 if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
} }
} }
xrf.getLastModel = () => xrf.model.last xrf.getLastModel = () => xrf.model.last
@ -940,7 +943,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
function nocollide(){ function nocollide(){
if( nocollide.tid ) return // ratelimit if( nocollide.tid ) return // ratelimit
_event.type = "nocollide" _event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) ) scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 ) 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}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene}) xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view) // spec: 2. init metadata
hashbus.pub( url, model ) // and eval URI XR fragments 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.add( model.scene )
xrf.navigator.updateHash(hash) xrf.navigator.updateHash(hash)
resolve(model) resolve(model)
@ -1096,10 +1103,15 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) 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) // this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group() xrf.focusLine = new xrf.THREE.Group()
xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true}) xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true})
@ -1398,7 +1410,7 @@ xrf.frag.href = function(v, opts){
vec4 color = texture2D(pano, sampleUV); vec4 color = texture2D(pano, sampleUV);
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js) // 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; 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; gl_FragColor = grayscale_color;
} }
`, `,
@ -1426,14 +1438,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => { let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
if( mesh.material ){ xrf.interactive.objects.map( (o) => {
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state let newState = o.name == mesh.name ? state : false
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0 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 // update mouse cursor
if( !renderer.domElement.lastCursor ) if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree .emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state ) .then( () => mesh.selected = state )
@ -1481,7 +1497,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
} }
xrf.frag.updatePredefinedView = (opts) => { xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest // spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => { const selectionOfInterest = (frag,scene,mesh) => {
@ -1489,7 +1505,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection let oldSelection
if(!id) return id // important: ignore empty strings if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name // Selection of Interest if predefined_view matches object name
if( mesh.visible ){ if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag}) xrf.emit('focus',{...opts,frag})
.then( () => { .then( () => {
const color = new THREE.Color(); const color = new THREE.Color();
@ -1557,14 +1573,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) ) remove.map( (n) => scene.remove(n.selection) )
// create new selections // create new selections
match.map( (w) => { match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){ if( w.key == `#${id}` ){
// if value is alias, execute fragment value if( w.value && w.value[0] == '#' ){
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR ) // 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) => { w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
}) })
} }
@ -1574,6 +1589,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src") console.log("filtering predefined view of src")
console.dir(frag) console.dir(frag)
}else{ }else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) { for ( let i in frag ) {
let v = frag[i] let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){ if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -1588,7 +1605,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url // react to enduser typing url
xrf.addEventListener('hash', (opts) => { xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) 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}) xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
}) })
@ -1627,10 +1643,8 @@ xrf.frag.q = function(v, opts){
xrf.frag.q.filter = function(scene,frag){ xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries // spec: https://xrfragment.org/#queries
let q = frag.q.query let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => { scene.traverse( (mesh) => {
for ( let i in q ) { for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId 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.) 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){ xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment 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") console.log(" └ instancing src")
let src; let src;
let url = v.string 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 opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
const addScene = (scene,url,frag) => { const addScene = (scene,url,frag) => {
src = xrf.frag.src.filterScene(scene,{...opts,frag}) src = xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( src, opts, url ) xrf.frag.src.scale( src, opts, url )
xrf.frag.src.eval( src, opts, url ) xrf.frag.src.eval( src, opts, url )
enableSourcePortation(src)
mesh.add(src) mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false 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) => { const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
@ -1690,8 +1722,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error ) .catch( console.error )
} }
if( url[0] == "#" ) addScene(scene,url,frag) // current file if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,frag) // external file else externalSRC(url,vfrag) // external file
} }
xrf.frag.src.eval = function(scene, opts, url){ 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> <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> 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> 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> <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> 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> XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:<br></p>
<ol> <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>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> <li>unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents</li>
</ol> </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> <p>Traditional webbrowsers can become 4D document-ready by:</p>
<ul> <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>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> <li>collapsing the 3D scene to an wordgraph (for essential navigation purposes) controllable thru a hash(tag)bus</li>
</ul> </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> <h1 id="conventions-and-definitions">Conventions and Definitions</h1>
@ -275,9 +275,9 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<tr> <tr>
<td><code>#t</code></td> <td><code>#t</code></td>
<td>vector2</td> <td>vector3</td>
<td><code>#t=500,1000</code></td> <td><code>#t=1,500,1000</code></td>
<td>sets animation-loop range between frame 500 and 1000</td> <td>play animation-loop range between frame 500 and 1000, at normal speed</td>
</tr> </tr>
<tr> <tr>
@ -307,22 +307,6 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</thead> </thead>
<tbody> <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> <tr>
<td><code>href</code></td> <td><code>href</code></td>
<td>string</td> <td>string</td>
@ -338,16 +322,26 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<td>XR embed / teleport</td> <td>XR embed / teleport</td>
<td>custom property in 3D fileformats</td> <td>custom property in 3D fileformats</td>
</tr> </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> </tbody>
</table> </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> <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> <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> </blockquote>
<h1 id="spatial-referencing-3d">Spatial Referencing 3D</h1> <h1 id="spatial-referencing-3d">Spatial Referencing 3D</h1>
<p>XR Fragments assume the following objectname-to-URIFragment mapping:</p>
<pre><code> <pre><code>
my.io/scene.fbx my.io/scene.fbx
+─────────────────────────────+ +─────────────────────────────+
@ -366,6 +360,10 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</code></pre> </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> <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> 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> </ul>
<blockquote> <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> </blockquote>
<h1 id="navigating-3d">Navigating 3D</h1> <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> <tr>
<td><b>#t</b>=0,100</td> <td><b>#t</b>=0,100</td>
<td>vector2</td> <td>vector3</td>
<td>(re)position looprange of scene-animation or <code>src</code>-mediacontent</td> <td>set playback speed, and (re)position looprange of scene-animation or <code>src</code>-mediacontent</td>
</tr> </tr>
<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>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>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>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> <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> </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/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> <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> <p>navigation, portals &amp; mutations</p>
@ -556,7 +554,7 @@ Resizing will be happen accordingly to its placeholder object <code>aquariumcube
</table> </table>
<ol> <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> <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> <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> </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> <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>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> <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> </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> <p>For example:</p>
<pre><code> +────────────────────────────────────────────────────────+ <pre><code> +────────────────────────────────────────────────────────+
@ -1105,19 +1107,45 @@ here are some hashtagbibs followed by bibtex:
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │ │ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not) │ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed. │ └ 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@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@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │ │ │
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+
</code></pre> </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> <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> <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> <hr>
<p><strong>Q:</strong> Why isn&rsquo;t there support for scripting <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, 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> <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> <h1 id="iana-considerations">IANA Considerations</h1>

View File

@ -199,17 +199,19 @@ sub-delims = "," / "="
| key | type | example (JSON) | function | existing compatibility | | 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 | | `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. 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 # Spatial Referencing 3D
XR Fragments assume the following objectname-to-URIFragment mapping:
``` ```
my.io/scene.fbx 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> 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: 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 Engineering Task Force L.R. van Kammen
Internet-Draft 22 September 2023 Internet-Draft 12 October 2023
Intended status: Informational Intended status: Informational
@ -13,9 +13,10 @@ Intended status: Informational
Abstract Abstract
This draft is a specification for 4D URLs & navigation, which links This draft is a specification for 4D URLs & hypermediatic
together space, time & text together, for hypermedia browsers with- (https://github.com/coderofsalvation/hypermediatic) navigation, which
or without a network-connection. links together space, time & text together, for hypermedia browsers
with- or without a network-connection.
The specification promotes spatial addressibility, sharing, The specification promotes spatial addressibility, sharing,
navigation, query-ing and annotating interactive (text)objects across navigation, query-ing and annotating interactive (text)objects across
for (XR) Browsers. for (XR) Browsers.
@ -41,7 +42,7 @@ Status of This Memo
time. It is inappropriate to use Internet-Drafts as reference time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress." 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 Copyright Notice
@ -52,10 +53,9 @@ Copyright Notice
van Kammen Expires 14 April 2024 [Page 1]
van Kammen Expires 25 March 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 This document is subject to BCP 78 and the IETF Trust's Legal
@ -79,7 +79,7 @@ Table of Contents
7. Navigating 3D . . . . . . . . . . . . . . . . . . . . . . . . 8 7. Navigating 3D . . . . . . . . . . . . . . . . . . . . . . . . 8
8. Top-level URL processing . . . . . . . . . . . . . . . . . . 9 8. Top-level URL processing . . . . . . . . . . . . . . . . . . 9
9. Embedding XR content (src-instancing) . . . . . . . . . . . . 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.1. UX spec . . . . . . . . . . . . . . . . . . . . . . . . 13
10.2. Scaling instanced content . . . . . . . . . . . . . . . 13 10.2. Scaling instanced content . . . . . . . . . . . . . . . 13
11. XR Fragment queries . . . . . . . . . . . . . . . . . . . . . 14 11. XR Fragment queries . . . . . . . . . . . . . . . . . . . . . 14
@ -90,12 +90,13 @@ Table of Contents
13.1. Default Data URI mimetype . . . . . . . . . . . . . . . 20 13.1. Default Data URI mimetype . . . . . . . . . . . . . . . 20
13.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 21 13.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 21
13.3. XR Text example parser . . . . . . . . . . . . . . . . . 22 13.3. XR Text example parser . . . . . . . . . . . . . . . . . 22
14. Broken links . . . . . . . . . . . . . . . . . . . . . . . . 24 14. Transclusion (broken link) resolution . . . . . . . . . . . . 24
15. Security Considerations . . . . . . . . . . . . . . . . . . . 25 15. Topic-based index-less Webrings . . . . . . . . . . . . . . . 25
16. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 16. Security Considerations . . . . . . . . . . . . . . . . . . . 26
17. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 26 17. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
18. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 26 18. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 27
19. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 26 19. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 27
20. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 27
1. Introduction 1. Introduction
@ -103,20 +104,23 @@ Table of Contents
introducing new dataformats? introducing new dataformats?
Historically, there's many attempts to create the ultimate Historically, there's many attempts to create the ultimate
markuplanguage or 3D fileformat. 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 XR Fragments allows us to enrich/connect existing dataformats, by
introducing existing technologies/ideas: introducing existing technologies/ideas:
1. addressibility and hypermediatic
(https://github.com/coderofsalvation/hypermediatic) navigation of
van Kammen Expires 25 March 2024 [Page 2] 3D scenes/objects: URI Fragments (https://en.wikipedia.org/wiki/
URI_fragment) + src/href spatial metadata
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
2. Interlinking text/& 3D by collapsing space into a Word Graph 2. Interlinking text/& 3D by collapsing space into a Word Graph
(XRWG) to show visible links (#visible-links) (and augmenting (XRWG) to show visible links (#visible-links) (and augmenting
text with bibs (https://github.com/coderofsalvation/tagbibs) / text with bibs (https://github.com/coderofsalvation/tagbibs) /
@ -161,13 +165,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 14 April 2024 [Page 3]
van Kammen Expires 25 March 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: 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). HTML).
* allowing assets to publish hashtags to themselves (the scene) * allowing assets to publish hashtags to themselves (the scene)
using the hashbus (like hashtags controlling the scrollbar). using the hashbus (like hashtags controlling the scrollbar).
* collapsing the 3D scene to an wordgraph (for essential navigation * collapsing the 3D scene to an wordgraph (for essential navigation
purposes) controllable thru a hash(tag)bus purposes) controllable thru a hash(tag)bus
XR Fragments itself are HTML-agnostic, though pseudo-XR Fragment XR Fragments itself are hypermediatic
browsers *can* be implemented on top of HTML/Javascript. (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 3. Conventions and Definitions
@ -274,12 +277,9 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 14 April 2024 [Page 5]
van Kammen Expires 25 March 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 4. List of URI Fragments
+==========+=========+==============+==============================+ +==========+=========+===============+==============================+
| fragment | type | example | info | | fragment | type | example | info |
+==========+=========+==============+==============================+ +==========+=========+===============+==============================+
| #pos | vector3 | #pos=0.5,0,0 | positions camera (or XR | | #pos | vector3 | #pos=0.5,0,0 | positions camera (or XR |
| | | | floor) to xyz-coord 0.5,0,0, | | | | | floor) to xyz-coord |
+----------+---------+--------------+------------------------------+ | | | | 0.5,0,0, |
| #rot | vector3 | #rot=0,90,0 | rotates camera 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 | | #t | vector3 | #t=1,500,1000 | play animation-loop range |
+----------+---------+--------------+------------------------------+ | | | | between frame 500 and |
| #...... | string | #.cubes | predefined views, XRWG | | | | | 1000, at normal speed |
| | | #cube | fragments and ID fragments | +----------+---------+---------------+------------------------------+
+----------+---------+--------------+------------------------------+ | #...... | 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 | xyz coordinates are similar to ones found in SVG Media Fragments
5. List of metadata for 3D nodes 5. List of metadata for 3D nodes
+======+========+=============+===========+=========================+ +======+========+==========+==================+===================+
| key | type | example | function | existing compatibility | | key | type | example | function | existing |
| | | (JSON) | | | | | | (JSON) | | compatibility |
+======+========+=============+===========+=========================+ +======+========+==========+==================+===================+
| name | string | "name": | identify/ | object supported in all | | href | string | "href": | XR teleport | custom property |
| | | "cube" | tag | 3D fileformats & scenes | | | | "b.gltf" | | in 3D fileformats |
+------+--------+-------------+-----------+-------------------------+ +------+--------+----------+------------------+-------------------+
| tag | string | "tag": | tag | custom property in 3D | | src | string | "src": | XR embed / | custom property |
| | | "cubes | object | fileformats | | | | "#cube" | teleport | in 3D fileformats |
| | | geo" | | |
+------+--------+-------------+-----------+-------------------------+
| href | string | "href": | XR | custom property in 3D |
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 | +------+--------+----------+------------------+-------------------+
+------+--------+-------------+-----------+-------------------------+ | tag | string | "tag": | tag object (for | custom property |
| src | string | "src": | XR embed | custom property in 3D | | | | "cubes | query-use / XRWG | in 3D fileformats |
| | | "#cube" | / | fileformats | | | | geo" | highlighting) | |
| | | | teleport | | +------+--------+----------+------------------+-------------------+
+------+--------+-------------+-----------+-------------------------+
Table 4 Table 4
Supported popular compatible 3D fileformats: .gltf, .obj, .fbx, Supported popular compatible 3D fileformats: .gltf, .obj, .fbx,
.usdz, .json (THREE.js), .dae and so on. .usdz, .json (THREE.js), .dae and so on.
| NOTE: XR Fragments are file-agnostic, which means that the | NOTE: XR Fragments are optional but also file- and protocol-
| metadata exist in programmatic 3D scene(nodes) too. | agnostic, which means that programmatic 3D scene(nodes) can also
| use the mechanism/metadata.
6. Spatial Referencing 3D 6. Spatial Referencing 3D
XR Fragments assume the following objectname-to-URIFragment mapping:
my.io/scene.fbx my.io/scene.fbx
+─────────────────────────────+ +─────────────────────────────+
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor) │ 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 Clever nested design of 3D scenes allow great ways for re-using
content, and/or previewing scenes. content, and/or previewing scenes.
For example, to render a portal with a preview-version of the scene, 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 * href: https://scene.fbx
* src: https://otherworld.gltf#mainobject * 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 | It also allows *sourceportation*, which basically means the
| enduser can teleport to the original XR Document of an src | enduser can teleport to the original XR Document of an src
| embedded object, and see a visible connection to the particular | embedded object, and see a visible connection to the particular
| embedded object. | embedded object. Basically an embedded link becoming an outbound
| link by activating it.
van Kammen Expires 25 March 2024 [Page 7]
Internet-Draft XR Fragments September 2023
7. Navigating 3D 7. Navigating 3D
@ -401,7 +407,8 @@ Internet-Draft XR Fragments September 2023
+====================+=========+==================================+ +====================+=========+==================================+
| <b>#pos</b>=0,0,0 | vector3 | (re)position camera | | <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 | | | | animation or src-mediacontent |
+--------------------+---------+----------------------------------+ +--------------------+---------+----------------------------------+
| <b>#rot</b>=0,90,0 | vector3 | rotate camera | | <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 2. set the position of the camera accordingly to the vector3 values
of #pos of #pos
3. rot sets the rotation of the camera (only for non-VR/AR headsets) 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 4. t sets the playbackspeed and animation-range of the current scene
src-mediacontent (video/audioframes e.g., use t=7,7 to 'STOP' at animation(s) or src-mediacontent (video/audioframes e.g., use
certain frame) 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 5. in case an href does not mention any pos-coordinate, pos=0,0,0
will be assumed will be assumed
Here's an ascii representation of a 3D scene-graph which contains 3D Here's an ascii representation of a 3D scene-graph which contains 3D
objects &#9723; and their metadata: objects &#9723; and their metadata:
van Kammen Expires 14 April 2024 [Page 8]
Internet-Draft XR Fragments October 2023
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+
│ │ │ │
│ index.gltf │ │ 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- An XR Fragment-compatible browser viewing this scene, allows the end-
user to interact with the buttonA and buttonB. user to interact with the buttonA and buttonB.
In case of buttonA the end-user will be teleported to another 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 14 April 2024 [Page 9]
van Kammen Expires 25 March 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 | 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 &#187; example implementation
@ -627,7 +627,7 @@ Internet-Draft XR Fragments September 2023
&#187; discussion (https://github.com/coderofsalvation/xrfragment/ &#187; discussion (https://github.com/coderofsalvation/xrfragment/
issues/4) issues/4)
10. Navigating content (href portals) 10. Navigating content (internal/outbound href portals)
navigation, portals & mutations navigation, portals & mutations
@ -641,9 +641,9 @@ Internet-Draft XR Fragments September 2023
Table 7 Table 7
1. clicking an ''external''- or ''file URI'' fully replaces the 1. clicking an outbound ''external''- or ''file URI'' fully replaces
current scene and assumes pos=0,0,0&rot=0,0,0 by default (unless the current scene and assumes pos=0,0,0&rot=0,0,0 by default
specified) (unless specified)
2. relocation/reorientation should happen locally for local URI's 2. relocation/reorientation should happen locally for local URI's
(#pos=....) (#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 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 | 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 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 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 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) 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 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 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 * 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 // 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 = ` 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 | nonmatching tags (@book{nonmatchingbook e.g.) should be performed
| and prompt the enduser for deleting them. | 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 In spirit of Ted Nelson's 'transclusion resolution', there's a soft-
various ways: mechanism to harden links & minimize broken links in various ways:
1. defining a different transport protocol (https vs ipfs or DAT) in 1. defining a different transport protocol (https vs ipfs or DAT) in
src or href values can make a difference 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 2. mirroring files on another protocol using (HTTP) errorcode tags
href properties in src or href properties
3. in case of src: nesting a copy of the embedded object in the 3. in case of src: nesting a copy of the embedded object in the
placeholder object (embeddedObject) will not be replaced when the placeholder object (embeddedObject) will not be replaced when the
request fails 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: For example:
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+
@ -1362,18 +1366,53 @@ Internet-Draft XR Fragments September 2023
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │ │ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not) │ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed. │ └ 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@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@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 Since XR Text contains metadata too, the user should be able to set
up tagging-rules, so the copy-paste feature can : 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 * filter out sensitive data when copy/pasting (XR text with
tag:secret e.g.) tag:secret e.g.)
16. FAQ 17. FAQ
*Q:* Why is everything HTTP GET-based, what about POST/PUT/DELETE *Q:* Why is everything HTTP GET-based, what about POST/PUT/DELETE
HATEOS HATEOS
@ -1390,38 +1429,50 @@ Internet-Draft XR Fragments September 2023
(for example, an XR Hypermedia browser can decide to support (for example, an XR Hypermedia browser can decide to support
POST/PUT/DELETE requests for embedded HTML thru src values) POST/PUT/DELETE requests for embedded HTML thru src values)
*Q:* Why isn't there support for scripting *A:* This is out of scope, *Q:* Why isn't there support for scripting, while we have things like
and up to the XR hypermedia browser. Javascript seems to been able WASM *A:* This is out of scope as it unhyperifies hypermedia, and
to turn webpages from hypermedia documents into its opposite this is up to XR hypermedia browser-extensions.
(hyperscripted nonhypermedia documents). In order to prevent this 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
van Kammen Expires 25 March 2024 [Page 25] liberate people from finnicky scripting) XR Fragments should never
unhyperify itself by hardcoupling to a particular markup or scripting
Internet-Draft XR Fragments September 2023 language. XR Macro's (https://xrfragment.org/doc/RFC_XR_Macros.html)
are an example of something which is probably smarter and safer for
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 hypermedia browsers to implement, instead of going full-in with a
turing-complete scripting language (and suffer the security turing-complete scripting language (and suffer the security
consequences later). 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. This document has no IANA actions.
18. Acknowledgments 19. Acknowledgments
* NLNET (https://nlnet.nl) * NLNET (https://nlnet.nl)
* Future of Text (https://futureoftext.org) * Future of Text (https://futureoftext.org)
* visual-meta.info (https://visual-meta.info) * visual-meta.info (https://visual-meta.info)
19. Appendix: Definitions 20. Appendix: Definitions
+=================+===============================================+ +=================+===============================================+
| definition | explanation | | definition | explanation |
@ -1450,19 +1501,19 @@ Internet-Draft XR Fragments September 2023
| spacetime | positions camera, triggers scene-preset/time | | spacetime | positions camera, triggers scene-preset/time |
| hashtags | | | hashtags | |
+-----------------+-----------------------------------------------+ +-----------------+-----------------------------------------------+
van Kammen Expires 25 March 2024 [Page 26]
Internet-Draft XR Fragments September 2023
| teleportation | repositioning the enduser to a different | | teleportation | repositioning the enduser to a different |
| | position (or 3D scene/file) | | | position (or 3D scene/file) |
+-----------------+-----------------------------------------------+ +-----------------+-----------------------------------------------+
| sourceportation | teleporting the enduser to the original XR | | sourceportation | teleporting the enduser to the original XR |
| | Document of an src embedded object. | | | 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 | | placeholder | a 3D object which with src-metadata (which |
| object | will be replaced by the src-data.) | | object | will be replaced by the src-data.) |
@ -1506,14 +1557,6 @@ Internet-Draft XR Fragments September 2023
| BibTag | a BibTeX tag | | BibTag | a BibTeX tag |
+-----------------+-----------------------------------------------+ +-----------------+-----------------------------------------------+
| (hashtag)bibs | an easy to speak/type/scan tagging SDL (see | | (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/ | | | here (https://github.com/coderofsalvation/ |
| | hashtagbibs) which expands to BibTex/JSON/XML | | | hashtagbibs) which expands to BibTex/JSON/XML |
+-----------------+-----------------------------------------------+ +-----------------+-----------------------------------------------+
@ -1522,47 +1565,4 @@ Internet-Draft XR Fragments September 2023
van Kammen Expires 14 April 2024 [Page 28]
van Kammen Expires 25 March 2024 [Page 28]

View File

@ -10,7 +10,7 @@
<workgroup>Internet Engineering Task Force</workgroup> <workgroup>Internet Engineering Task Force</workgroup>
<abstract> <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 /> 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 /> 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 /> XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:<br />
</t> </t>
<ol spacing="compact"> <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>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> <li>unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents</li>
</ol> </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> <t>Traditional webbrowsers can become 4D document-ready by:</t>
<ul spacing="compact"> <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>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> <li>collapsing the 3D scene to an wordgraph (for essential navigation purposes) controllable thru a hash(tag)bus</li>
</ul> </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>
<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name> <section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
@ -200,9 +200,9 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<tr> <tr>
<td><tt>#t</tt></td> <td><tt>#t</tt></td>
<td>vector2</td> <td>vector3</td>
<td><tt>#t=500,1000</tt></td> <td><tt>#t=1,500,1000</tt></td>
<td>sets animation-loop range between frame 500 and 1000</td> <td>play animation-loop range between frame 500 and 1000, at normal speed</td>
</tr> </tr>
<tr> <tr>
@ -228,22 +228,6 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
</thead> </thead>
<tbody> <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> <tr>
<td><tt>href</tt></td> <td><tt>href</tt></td>
<td>string</td> <td>string</td>
@ -259,12 +243,21 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
<td>XR embed / teleport</td> <td>XR embed / teleport</td>
<td>custom property in 3D fileformats</td> <td>custom property in 3D fileformats</td>
</tr> </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> </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> </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> </blockquote></section>
<section anchor="spatial-referencing-3d"><name>Spatial Referencing 3D</name> <section anchor="spatial-referencing-3d"><name>Spatial Referencing 3D</name>
<t>XR Fragments assume the following objectname-to-URIFragment mapping:</t>
<artwork> <artwork>
my.io/scene.fbx my.io/scene.fbx
@ -283,7 +276,8 @@ sub-delims = &quot;,&quot; / &quot;=&quot;
+─────────────────────────────+ +─────────────────────────────+
</artwork> </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> 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>href: <tt>https://scene.fbx</tt></li>
<li>src: <tt>https://otherworld.gltf#mainobject</tt></li> <li>src: <tt>https://otherworld.gltf#mainobject</tt></li>
</ul> </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> </blockquote></section>
<section anchor="navigating-3d"><name>Navigating 3D</name> <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> <tr>
<td>&lt;b&gt;#t&lt;/b&gt;=0,100</td> <td>&lt;b&gt;#t&lt;/b&gt;=0,100</td>
<td>vector2</td> <td>vector3</td>
<td>(re)position looprange of scene-animation or <tt>src</tt>-mediacontent</td> <td>set playback speed, and (re)position looprange of scene-animation or <tt>src</tt>-mediacontent</td>
</tr> </tr>
<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>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>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>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> <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> </ol>
<t>Here's an ascii representation of a 3D scene-graph which contains 3D objects <tt></tt> and their metadata:</t> <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> </t>
</section> </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> <t>navigation, portals &amp; mutations</t>
<table> <table>
<thead> <thead>
@ -473,7 +467,7 @@ Resizing will be happen accordingly to its placeholder object <tt>aquariumcube</
</tbody> </tbody>
</table> </table>
<ol> <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>
<li><t>relocation/reorientation should happen locally for local URI's (<tt>#pos=....</tt>)</t> <li><t>relocation/reorientation should happen locally for local URI's (<tt>#pos=....</tt>)</t>
</li> </li>
@ -943,15 +937,16 @@ here are some hashtagbibs followed by bibtex:
</blockquote></section> </blockquote></section>
</section> </section>
<section anchor="broken-links"><name>Broken links</name> <section anchor="transclusion-broken-link-resolution"><name>Transclusion (broken link) resolution</name>
<t>There's a soft-mechanism to harden links &amp; prevent broken links in various ways:</t> <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"> <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>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> <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> </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> +────────────────────────────────────────────────────────+ <artwork> +────────────────────────────────────────────────────────+
│ │ │ │
@ -961,20 +956,43 @@ here are some hashtagbibs followed by bibtex:
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href!404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href!400: #q=clienterrortext │ │ │ └ href@400: #q=clienterrortext │
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not) │ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed. │ └ 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@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@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │ │ │
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+
</artwork> </artwork>
</section> </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> <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> <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 /> <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> <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 <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, 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> <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>
<section anchor="iana-considerations"><name>IANA Considerations</name> <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> 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> 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> <h2 id="usecase-click-object">Usecase: click object</h2>
<table> <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> 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> 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 ## Usecase: click object
| custom property | value | trigger when | | custom property | value | trigger when |

View File

@ -3,7 +3,7 @@
Internet Engineering Task Force L.R. van Kammen Internet Engineering Task Force L.R. van Kammen
Internet-Draft 22 September 2023 Internet-Draft 12 October 2023
Intended status: Informational Intended status: Informational
@ -38,7 +38,7 @@ Status of This Memo
time. It is inappropriate to use Internet-Drafts as reference time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress." 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 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 extracted from this document must include Revised BSD License text as
@ -68,17 +68,17 @@ Table of Contents
2. Core principle . . . . . . . . . . . . . . . . . . . . . . . 2 2. Core principle . . . . . . . . . . . . . . . . . . . . . . . 2
3. Conventions and Definitions . . . . . . . . . . . . . . . . . 3 3. Conventions and Definitions . . . . . . . . . . . . . . . . . 3
4. List of XR Macros . . . . . . . . . . . . . . . . . . . . . . 3 4. List of XR Macros . . . . . . . . . . . . . . . . . . . . . . 3
4.1. Usecase: click object . . . . . . . . . . . . . . . . . . 3 4.1. Usecase: click object . . . . . . . . . . . . . . . . . . 4
4.2. Usecase: conditional click object . . . . . . . . . . . . 3 4.2. Usecase: conditional click object . . . . . . . . . . . . 4
4.3. Usecase: click object (roundrobin) . . . . . . . . . . . 4 4.3. Usecase: click object (roundrobin) . . . . . . . . . . . 5
4.4. Usecase: click object or URI fragment, and scene load 4.4. Usecase: click object or URI fragment, and scene load
trigger . . . . . . . . . . . . . . . . . . . . . . . . . 4 trigger . . . . . . . . . . . . . . . . . . . . . . . . . 5
4.5. Usecase: present context menu with options . . . . . . . 5 4.5. Usecase: present context menu with options . . . . . . . 6
4.6. Event Bubble-flow . . . . . . . . . . . . . . . . . . . . 5 4.6. Event Bubble-flow . . . . . . . . . . . . . . . . . . . . 6
5. Security Considerations . . . . . . . . . . . . . . . . . . . 6 5. Security Considerations . . . . . . . . . . . . . . . . . . . 7
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 6 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 7
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 6 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 7
8. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 6 8. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 7
1. Introduction 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 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 Macros also act as events, so more serious scripting languages can
react to them as well. 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 4.1. Usecase: click object
+=================+================+================+ +=================+================+================+
@ -152,7 +207,7 @@ Internet-Draft XR Macros September 2023
| !clickme | bg=1,1,1&foo=2 | object clicked | | !clickme | bg=1,1,1&foo=2 | object clicked |
+-----------------+----------------+----------------+ +-----------------+----------------+----------------+
Table 1 Table 2
4.2. Usecase: conditional click object 4.2. Usecase: conditional click object
@ -162,17 +217,16 @@ Internet-Draft XR Macros September 2023
| # | foo=1 | scene | | # | foo=1 | scene |
+-----------------+------------------+----------------------------+ +-----------------+------------------+----------------------------+
| !clickme | q=foo>2&bg=1,1,1 | object clicked and foo > 2 | | !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 | 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 | 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 | | night | bg=0,0,0&foo=2 | roundrobin |
+-----------------+----------------+----------------+ +-----------------+----------------+----------------+
Table 3 Table 4
| when a user clicks an object with the custom properties above, it | when a user clicks an object with the custom properties above, it
| should trigger either day noon or night in roundrobin fashion. | 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 | | 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 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 | | !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 | When interacting with an object with more than one !-macro, the XR
| Browser should offer a contextmenu to execute a macro. | 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 | 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 | | | 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 /> Macros also act as events, so more serious scripting languages can react to them as well.<br />
</t> </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> <section anchor="usecase-click-object"><name>Usecase: click object</name>
<table> <table>
<thead> <thead>

View File

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

View File

@ -73,7 +73,6 @@ input[type="submit"] {
} }
#overlay > #uri { #overlay > #uri {
display:none;
height: 29px; height: 29px;
font-size: 21px; font-size: 21px;
position: absolute; position: absolute;
@ -92,25 +91,40 @@ input[type="submit"] {
border: 5px solid #1c1c3299; border: 5px solid #1c1c3299;
padding: 0px 6px; padding: 0px 6px;
position: absolute; position: absolute;
font-weight: bold; font-weight: 1000;
font-family: sans-serif; font-family: sans-serif;
color: #7c7c7c; font-size:17px;
color: #888;
height:43px;
z-index:2000; z-index:2000;
cursor:pointer; cursor:pointer;
right: 10px; right: 10px;
text-transform:uppercase;
} }
a.btn-foot#embed{ a.btn-foot#embed{
color: #888; color: #888;
bottom: 126px; bottom: 129px;
} }
a.btn-foot#model{ a.btn-foot#model{
bottom:73px; bottom:72px;
}
a.btn-foot#more{
bottom:72px;
width: 60px;
text-align: center;
} }
a.btn-foot#source{ a.btn-foot#source{
bottom:179px; bottom:186px;
}
a#source,
a#embed,
a#model{
display:none
} }
html.a-fullscreen a.btn-foot { html.a-fullscreen a.btn-foot {
@ -251,3 +265,7 @@ html{
background-color: #4443; 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){ export function setupUrlBar(el,XRF){
var isIframe = document.location.hash.match(/embed=1/)
var isIframe = (window === window.parent || window.opener) ? false : true; let ids = ['#overlay','a#embed','a#source','a#model']
if( isIframe || document.location.href.match(/localhost/) ){ let showButtons = () => {
// show internal URL bar to test XR fragments interactively ids.map( (i) => $(i).style.display = 'block' )
el.style.display = 'block' $('a#more').style.display = 'none'
XRF.addEventListener('updateHash', () => reflectUrl() )
const reflectUrl = (url) => el.value = url || document.location.search.substr(1) + document.location.hash
reflectUrl()
} }
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) { function SnackBar(userOptions) {

View File

@ -10,12 +10,15 @@
<body> <body>
<div id="overlay" x-data="{ urls: ['#pos=0,1.6,15','#pos=0,1.6,15&rot=0,360,0'] }"> <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"/> <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 )"/> <input type="text" id="uri" value="" onchange="XRF.navigator.to( $('#uri').value )"/>
</div> </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="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="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="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> <textarea style="display:none"></textarea>
<script async src="./../../assets/js/alpine.min.js"></script> <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 aScene = document.querySelector('a-scene')
let XRF = AFRAME.XRF = xrf.init({ let XRF = AFRAME.XRF = xrf.init({
THREE, THREE,
camera: aScene.camera, camera: aScene.camera,
scene: aScene.object3D, scene: aScene.object3D,
renderer: aScene.renderer, renderer: aScene.renderer,
debug: true, debug: true,
loaders: { loaders: {
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments? 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(){ function nocollide(){
if( nocollide.tid ) return // ratelimit if( nocollide.tid ) return // ratelimit
_event.type = "nocollide" _event.type = "nocollide"
scope.children.map( (c) => c.dispatchEvent(_event) ) scope.objects.map( (c) => c.dispatchEvent(_event) )
nocollide.tid = setTimeout( () => nocollide.tid = null, 100 ) 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 pub.fragment = (k, opts ) => { // evaluate one fragment
let frag = opts.frag[k]; let frag = opts.frag[k];
// call native function (xrf/env.js e.g.), or pass it to user decorator // call native function (xrf/env.js e.g.), or pass it to user decorator
xrf.emit(k,opts) xrf.emit(k,opts)
.then( () => { .then( () => {

View File

@ -52,6 +52,7 @@ xrf.parseModel = function(model,url){
model.mixer = new xrf.THREE.AnimationMixer(model.scene) model.mixer = new xrf.THREE.AnimationMixer(model.scene)
model.animations.map( (anim) => { model.animations.map( (anim) => {
anim.action = model.mixer.clipAction( anim ) anim.action = model.mixer.clipAction( anim )
//anim.action.setLoop(0)
anim.action.play() 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.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.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; 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 if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
} }
} }
xrf.getLastModel = () => xrf.model.last 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}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedView({model,scene:model.scene}) xrf.frag.defaultPredefinedView({model,scene:model.scene})
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view) // spec: 2. init metadata
hashbus.pub( url, model ) // and eval URI XR fragments 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.add( model.scene )
xrf.navigator.updateHash(hash) xrf.navigator.updateHash(hash)
resolve(model) resolve(model)
@ -49,10 +53,15 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) 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) // this allows selectionlines to be updated according to the camera (renderloop)
xrf.focusLine = new xrf.THREE.Group() xrf.focusLine = new xrf.THREE.Group()
xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true}) xrf.focusLine.material = new xrf.THREE.LineDashedMaterial({color:0xFF00FF,linewidth:3, scale: 1, dashSize: 0.2, gapSize: 0.1,opacity:0.3, transparent:true})

View File

@ -81,7 +81,7 @@ xrf.frag.href = function(v, opts){
vec4 color = texture2D(pano, sampleUV); vec4 color = texture2D(pano, sampleUV);
// Convert color to grayscale (lazy lite approach to not having to match tonemapping/shaderstacking of THREE.js) // 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; 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; gl_FragColor = grayscale_color;
} }
`, `,
@ -109,14 +109,18 @@ xrf.frag.href = function(v, opts){
let selected = (state) => () => { let selected = (state) => () => {
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
if( mesh.material ){ xrf.interactive.objects.map( (o) => {
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state let newState = o.name == mesh.name ? state : false
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0 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 // update mouse cursor
if( !renderer.domElement.lastCursor ) if( !renderer.domElement.lastCursor )
renderer.domElement.lastCursor = renderer.domElement.style.cursor renderer.domElement.lastCursor = renderer.domElement.style.cursor
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
xrf xrf
.emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree .emit('href',{selected:state,mesh,xrf:v}) // let all listeners agree
.then( () => mesh.selected = state ) .then( () => mesh.selected = state )

View File

@ -6,7 +6,7 @@ xrf.frag.defaultPredefinedView = (opts) => {
} }
xrf.frag.updatePredefinedView = (opts) => { xrf.frag.updatePredefinedView = (opts) => {
let {frag,scene,model} = opts let {frag,scene,model,renderer} = opts
// spec: https://xrfragment.org/#Selection%20of%20interest // spec: https://xrfragment.org/#Selection%20of%20interest
const selectionOfInterest = (frag,scene,mesh) => { const selectionOfInterest = (frag,scene,mesh) => {
@ -14,7 +14,7 @@ xrf.frag.updatePredefinedView = (opts) => {
let oldSelection let oldSelection
if(!id) return id // important: ignore empty strings if(!id) return id // important: ignore empty strings
// Selection of Interest if predefined_view matches object name // Selection of Interest if predefined_view matches object name
if( mesh.visible ){ if( mesh.visible && mesh.material){
xrf.emit('focus',{...opts,frag}) xrf.emit('focus',{...opts,frag})
.then( () => { .then( () => {
const color = new THREE.Color(); const color = new THREE.Color();
@ -82,14 +82,13 @@ xrf.frag.updatePredefinedView = (opts) => {
remove.map( (n) => scene.remove(n.selection) ) remove.map( (n) => scene.remove(n.selection) )
// create new selections // create new selections
match.map( (w) => { match.map( (w) => {
if( w.key == `#${id}` && w.value && w.value[0] == '#' ){ if( w.key == `#${id}` ){
// if value is alias, execute fragment value if( w.value && w.value[0] == '#' ){
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR ) // 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) => { w.nodes.map( (mesh) => selectionOfInterest( v, scene, mesh ) )
if( mesh.material )
selectionOfInterest( v, scene, mesh )
})
}) })
} }
@ -99,6 +98,8 @@ xrf.frag.updatePredefinedView = (opts) => {
console.log("filtering predefined view of src") console.log("filtering predefined view of src")
console.dir(frag) console.dir(frag)
}else{ }else{
console.log("updatePredefinedView")
console.dir(frag)
for ( let i in frag ) { for ( let i in frag ) {
let v = frag[i] let v = frag[i]
if( v.is( xrf.XRF.PV_EXECUTE ) ){ if( v.is( xrf.XRF.PV_EXECUTE ) ){
@ -113,7 +114,6 @@ xrf.frag.updatePredefinedView = (opts) => {
// react to enduser typing url // react to enduser typing url
xrf.addEventListener('hash', (opts) => { xrf.addEventListener('hash', (opts) => {
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) 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}) 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){ xrf.frag.q.filter = function(scene,frag){
// spec: https://xrfragment.org/#queries // spec: https://xrfragment.org/#queries
let q = frag.q.query let q = frag.q.query
console.dir(q)
scene.traverse( (mesh) => { scene.traverse( (mesh) => {
for ( let i in q ) { for ( let i in q ) {
if( i == '' ) continue
let isMeshId = q[i].id != undefined let isMeshId = q[i].id != undefined
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId 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.) 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){ xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment 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") console.log(" └ instancing src")
let src; let src;
let url = v.string 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 opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
const addScene = (scene,url,frag) => { const addScene = (scene,url,frag) => {
src = xrf.frag.src.filterScene(scene,{...opts,frag}) src = xrf.frag.src.filterScene(scene,{...opts,frag})
xrf.frag.src.scale( src, opts, url ) xrf.frag.src.scale( src, opts, url )
xrf.frag.src.eval( src, opts, url ) xrf.frag.src.eval( src, opts, url )
enableSourcePortation(src)
mesh.add(src) mesh.add(src)
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) mesh.traverse( (n) => n.isSRC = n.isXRF = true )
if( mesh.material ) mesh.material.visible = false 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) => { const externalSRC = (url,frag,src) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
@ -38,8 +56,8 @@ xrf.frag.src = function(v, opts){
.catch( console.error ) .catch( console.error )
} }
if( url[0] == "#" ) addScene(scene,url,frag) // current file if( url[0] == "#" ) addScene(scene,url,vfrag) // current file
else externalSRC(url,frag) // external file else externalSRC(url,vfrag) // external file
} }
xrf.frag.src.eval = function(scene, opts, url){ xrf.frag.src.eval = function(scene, opts, url){