diff --git a/doc/RFC_XR_Fragments.md b/doc/RFC_XR_Fragments.md
index 0cb13d4..2a46c57 100644
--- a/doc/RFC_XR_Fragments.md
+++ b/doc/RFC_XR_Fragments.md
@@ -94,8 +94,8 @@ value: draft-XRFRAGMENTS-leonvankammen-00
.# Abstract
This draft is a specification for 4D URLs & [hypermediatic](https://github.com/coderofsalvation/hypermediatic) navigation, which links together space, time & text together, for hypermedia browsers with- or without a network-connection.
-The specification promotes spatial addressibility, sharing, navigation, query-ing and annotating interactive (text)objects across for (XR) Browsers.
-XR Fragments allows us to enrich existing dataformats, by recursive use of existing proven technologies like [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment) and BibTags notation.
+The specification promotes spatial addressibility, sharing, navigation, query-ing and databinding objects for (XR) Browsers.
+XR Fragments allows us to enrich existing dataformats, by recursive use of existing metadata inside 3D scene(files), and proven technologies like [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment).
> Almost every idea in this document is demonstrated at [https://xrfragment.org](https://xrfragment.org)
@@ -103,34 +103,38 @@ XR Fragments allows us to enrich existing dataformats, by recursive use of exist
# Introduction
-How can we add more features to existing text & 3D scenes, without introducing new dataformats?
+How can we add more control to existing text & 3D scenes, without introducing new dataformats?
Historically, there's many attempts to create the ultimate markuplanguage or 3D fileformat.
-The lowest common denominator is: describing/tagging/naming nodes using **plain text**.
-XR Fragments allows us to enrich/connect existing dataformats, by introducing existing technologies/ideas:
+The lowest common denominator is: designers describing/tagging/naming things using **plain text**.
+XR Fragments exploits the fact that all 3D models already contain such metadata:
+
+**XR Fragments allows controlling of metadata in 3D scene(files) using URLs**
+
+Or more detailed:
1. addressibility and [hypermediatic](https://github.com/coderofsalvation/hypermediatic) navigation of 3D scenes/objects: [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment) + src/href spatial metadata
-1. Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) to show [visible links](#visible-links) (and augmenting text with [bibs](https://github.com/coderofsalvation/tagbibs) / [BibTags](https://en.wikipedia.org/wiki/BibTeX) appendices (see [visual-meta](https://visual-meta.info) e.g.)
+1. Interlinking (text)objects by collapsing space into a Word Graph (XRWG) to show [visible links](#visible-links)
1. unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents
> NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible
# Core principle
-XR Fragments strives to serve (nontechnical/fuzzy) humans first, and machine(implementations) later, by ensuring hasslefree text-vs-thought feedback loops.
-This also means that the repair-ability of machine-matters should be human friendly too (not too complex).
+**XR Fragments allows controlling of metadata in 3D scene(files) using URLs**
+
XR Fragments tries to seek to connect the world of text (semantical web / RDF), and the world of pixels.
-Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a more integrated path **towards** them, by describing how to make browsers **4D URL-ready**:
+Instead of combining them (in a game-editor e.g.), XR Fragments **integrates all**, by collecting metadata into an XRWG and control it via URL:
| principle | XR 4D URL | HTML 2D URL |
|----------------------|-------------------------------------------------|---------------------------------------|
| the XRWG | wordgraph (collapses 3D scene to tags) | Ctrl-F (find) |
-| the hashbus | hashtags map to camera/scene-projections | hashtags map to document positions |
-| spacetime hashtags | positions camera, triggers scene-preset/time | jumps/scrolls to chapter |
+| the hashbus | hashtags alter camera/scene/object-projections | hashtags alter document positions |
| src metadata | renders content and offers sourceportation | renders content |
| href metadata | teleports to other XR document | jumps to other HTML document |
-| href metadata | repositions camera or animation-range | jumps to camera |
-| href metadata | draws visible connection(s) for XRWG 'tag' | |
| href metadata | triggers predefined view | Media fragments |
+| href metadata | triggers camera/scene/object/projections | n/a |
+| href metadata | draws visible connection(s) for XRWG 'tag' | n/a |
+| href metadata | queries certain (in)visible objects | n/a |
> XR Fragments does not look at XR (or the web) thru the lens of HTML.
But approaches things from a higherlevel feedbackloop/hypermedia browser-perspective:
@@ -144,11 +148,14 @@ Instead of combining them (in a game-editor e.g.), XR Fragments is opting for a
│ 4D URL: ://park.com /4Dscene.fbx ──> ?misc ──> #view ───> hashbus │
│ │ #query │ │
│ │ #tag │ │
+ │ │ #material │ │
+ │ │ #animation │ │
+ │ │ #texture │ │
+ │ │ #variable │ │
│ │ │ │
│ XRWG <─────────────────────<────────────+ │
│ │ │ │
- │ ├─ objects ───────────────>────────────│ │
- │ └─ text ───────────────>────────────+ │
+ │ └─ objects ──────────────>────────────+ │
│ │
│ │
+──────────────────────────────────────────────────────────────────────────────────────────────+
@@ -180,22 +187,19 @@ sub-delims = "," / "="
| Demo | Explanation |
|-------------------------------|---------------------------------|
| `pos=1,2,3` | vector/coordinate argument e.g. |
-| `pos=1,2,3&rot=0,90,0&q=.foo` | combinators |
+| `pos=1,2,3&rot=0,90,0&q=foo` | combinators |
> this is already implemented in all browsers
# List of URI Fragments
-| fragment | type | example | info |
-|--------------|----------|-------------------|----------------------------------------------------------------------|
-| `#pos` | vector3 | `#pos=0.5,0,0` | positions camera (or XR floor) to xyz-coord 0.5,0,0, |
-| `#rot` | vector3 | `#rot=0,90,0` | rotates camera to xyz-coord 0.5,0,0 |
-| `#t` | vector3 | `#t=1,500,1000` | play animation-loop range between frame 500 and 1000, at normal speed|
-| `#......` | string | `#.cubes` `#cube` | predefined views, XRWG fragments and ID fragments |
+| fragment | type | example | info |
+|-------------------|----------|-------------------|----------------------------------------------------------------------|
+| `#pos` | vector3 | `#pos=0.5,0,0` | positions camera (or XR floor) to xyz-coord 0.5,0,0, |
+| `#rot` | vector3 | `#rot=0,90,0` | rotates camera to xyz-coord 0.5,0,0 |
+| `#t` | vector3 | `#t=1,500,1000` | play animation-loop range between frame 500 and 1000, at normal speed|
-> xyz coordinates are similar to ones found in SVG Media Fragments
-
-# List of metadata for 3D nodes
+## List of metadata for 3D nodes
| key | type | example (JSON) | function | existing compatibility |
|--------------|----------|------------------------|---------------------|----------------------------------------|
@@ -207,8 +211,22 @@ Supported popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `
> 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
+## Dynamic XR Fragments (databindings)
+These are automatic fragment-to-metadata mappings, which only trigger if the 3D scene metadata matches a specific identifier (`aliasname` e.g.)
+
+| fragment | type | example | info |
+|------------------------|----------|-------------------|-------------------------------------------------------------------------------|
+| `#` | string | `#cubes` | evaluate predefined views (`#cubes: #foo&bar` e.g.) |
+| `#` | string | `#person` | focus object(s) with `tag: person` or name `person` by looking up XRWG |
+| `#` | string | `#cam01` | set camera as active camera |
+| `#=x,x,x` | vector3 | `#myanim=1,1,0` | play (non-global) animation ID |
+| `#=`| string | `horizon=fader` | animate r/g/b/o(pacity) of material `horizon` with `fader` obj (xyzw=rgbo) |
+| `#=`| string | `page=scroller` | animate x/y/r(otation) of texture `page` with `scroller` object (xyz=xyr) |
+| `#=` | string|vector3 | `myvar=fader` | set/animate shaderuniform- or scene-specific vars with `fader` object (*) |
+
+# Spatial Referencing 3D
XR Fragments assume the following objectname-to-URIFragment mapping:
@@ -341,6 +359,8 @@ Resizing will be happen accordingly to its placeholder object `aquariumcube`, se
1. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored.
1. clicking on external `src`-values always allow sourceportation: teleporting to the origin URI to which the object belongs.
1. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0`
+1. equirectangular detection: when the width of an image is twice the height (aspect 2:1), an equirectangular projection is assumed.
+1. when the enduser clicks an href with `#t=1,0,0` (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.)
* `model/gltf+json`
* `image/png`
@@ -439,10 +459,11 @@ controls the animation(s) of the scene (or `src` resource which contains a timel
To play global audio/video items:
-* add a `src: foo.mp3` or `src: bar.mp4` metadata to a 3D object (`cube` e.g.)
-* to disable auto-play and global timeline ([[#t=|t]]) control: hardcode a [[#t=|t]] XR Fragment: (`src: bar.mp3#t=0,0,0` e.g.)
-* to play it, add `href: #cube` somewhere else
-* when the enduser clicks the `href`, `#t=1,0,0` (play) will be applied to the `src` value
+1. add a `src: foo.mp3` or `src: bar.mp4` metadata to a 3D object (`cube` e.g.)
+1. to disable auto-play and global timeline ([[#t=|t]]) control: hardcode a [[#t=|t]] XR Fragment: (`src: bar.mp3#t=0,0,0` e.g.)
+1. to play it, add `href: #cube` somewhere else
+1. when the enduser clicks the `href`, `#t=1,0,0` (play) will be applied to the `src` value
+1. to play a single animation, add href: #animationname=1,0,0 somewhere else
> NOTE: hardcoded framestart/framestop uses sampleRate/fps of embedded audio/video, otherwise the global fps applies. For more info see [[#t|t]].
diff --git a/src/3rd/js/aframe/index.js b/src/3rd/js/aframe/index.js
index e9094ec..6d29ac3 100644
--- a/src/3rd/js/aframe/index.js
+++ b/src/3rd/js/aframe/index.js
@@ -5,9 +5,6 @@ window.AFRAME.registerComponent('xrf', {
if( !AFRAME.XRF ){
document.querySelector('a-scene').addEventListener('loaded', () => {
- //window.addEventListener('popstate', clear )
- //window.addEventListener('pushstate', clear )
-
// enable XR fragments
let aScene = document.querySelector('a-scene')
let XRF = AFRAME.XRF = xrf.init({
@@ -30,7 +27,6 @@ window.AFRAME.registerComponent('xrf', {
if( frag.q ) return // camera was not targeted for rotation
let look = document.querySelector('[look-controls]')
if( look ) look.removeAttribute("look-controls")
- // camOverride(xrf,v,opts)
// *TODO* make look-controls compatible, because simply
// adding the look-controls will revert to the old rotation (cached somehow?)
//setTimeout( () => look.setAttribute("look-controls",""), 100 )
@@ -46,40 +42,17 @@ window.AFRAME.registerComponent('xrf', {
el.setAttribute("pressable", '') // detect hand-controller click
// add click
el.addEventListener("click", clickHandler )
- el.addEventListener("pressedstarted", clickHandler )
- // this.el.addEventListener("buttondown", console.dir )
- // this.el.addEventListener("touchstart", console.dir )
- // this.el.addEventListener("triggerdown", console.dir )
- // this.el.addEventListener("gripdown", console.dir )
- // this.el.addEventListener("abuttondown", console.dir )
- // this.el.addEventListener("pinchended", console.dir )
-
+ //el.addEventListener("pressedstarted", clickHandler )
$('a-scene').appendChild(el)
}
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
-// xrf.addEventListener('interactionReady', () => {
-// let raycasters = [ ...document.querySelectorAll('[raycaster]') ]
-// raycasters.map( (rc) => {
-// rc = rc.components['raycaster']
-// rc.refreshObjects = () => {
-// rc.objects = xrf.interactive.objects.map( (o) => ({ ...o, el:{} }) ) // AFRAME raycaster requires 'el' property
-// console.log("refreshing")
-// rc.dirty = false
-// }
-// rc.dirty = true
-// rc.refreshObjects()
-// })
-// })
-
-
// cleanup xrf-get objects when resetting scene
- xrf.reset = ((reset) => () => {
- reset()
+ xrf.addEventListener('reset', (opts) => {
console.log("aframe reset")
let els = [...document.querySelectorAll('[xrf-get]')]
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
- })(XRF.reset)
+ })
// undo lookup-control shenanigans (which blocks updating camerarig position in VR)
aScene.addEventListener('enter-vr', () => document.querySelector('[camera]').object3D.parent.matrixAutoUpdate = true )
diff --git a/src/3rd/js/aframe/xrf-gaze.js b/src/3rd/js/aframe/xrf-gaze.js
index ef03ef7..6a672a6 100644
--- a/src/3rd/js/aframe/xrf-gaze.js
+++ b/src/3rd/js/aframe/xrf-gaze.js
@@ -26,25 +26,29 @@ AFRAME.registerComponent('xrf-gaze',{
init:function(data){
this.immersive = false;
let enabled = () => AFRAME.utils.device.isMobile()
- let setVisible = () => document.querySelector('[cursor]').setAttribute('visible', enabled() )
+ let setVisible = () => {
+ let cursor = document.querySelector('[cursor]')
+ if( cursor ) cursor.setAttribute('visible', enabled() )
+ }
+
this.setGazer(enabled())
- if( enabled() ) setVisible();
+ setVisible();
document.querySelector("a-scene").addEventListener('exit-vr', () => {
this.immersive = false;
setVisible()
})
+
document.querySelector("a-scene").addEventListener('enter-vr', () => {
this.immersive = true;
setVisible()
if( !document.querySelector("#cursor") ) return
})
-
let highlightMesh = (state) => (e) => {
if( !e.target.object3D ) return
let obj = e.target.object3D.children[0]
- if( obj.userData && obj.userData.XRF && obj.userData.XRF.href )
+ if( obj && obj.userData && obj.userData.XRF && obj.userData.XRF.href )
obj.userData.XRF.href.selected( state )()
}
this.el.addEventListener("mouseenter", highlightMesh(true) )
diff --git a/src/3rd/js/aframe/xrf-get.js b/src/3rd/js/aframe/xrf-get.js
index e4133d1..797cf2c 100644
--- a/src/3rd/js/aframe/xrf-get.js
+++ b/src/3rd/js/aframe/xrf-get.js
@@ -1,7 +1,8 @@
window.AFRAME.registerComponent('xrf-get', {
schema: {
name: {type: 'string'},
- clone: {type: 'boolean', default:false}
+ clone: {type: 'boolean', default:false},
+ reparent: {type: 'boolean', default:false}
},
init: function () {
@@ -20,18 +21,26 @@ window.AFRAME.registerComponent('xrf-get', {
console.error("mesh with name '"+meshname+"' not found in model")
return;
}
- // convert to worldcoordinates
-// mesh.getWorldPosition(mesh.position)
-// mesh.getWorldScale(mesh.scale)
-// mesh.getWorldQuaternion(mesh.quaternion)
+ // we don't want to re-parent gltf-meshes
mesh.isXRF = true // mark for deletion by xrf
- this.el.object3D.add = (a) => a // dummy
+ if( this.data.reparent ){
+ const world = {
+ pos: new THREE.Vector3(),
+ scale: new THREE.Vector3(),
+ quat: new THREE.Quaternion()
+ }
+ mesh.getWorldPosition(world.pos)
+ mesh.getWorldScale(world.scale)
+ mesh.getWorldQuaternion(world.quat);
+ mesh.position.copy(world.pos)
+ mesh.scale.copy(world.scale)
+ mesh.setRotationFromQuaternion(world.quat);
+ }else{
+ // add() will reparent the mesh so lets create a dummy
+ this.el.object3D.add = (a) => a
+ }
this.el.setObject3D('mesh',mesh)
- // normalize position
- //this.el.object3D.position.copy( mesh.position )
- //mesh.position.fromArray([0,0,0])
if( !this.el.id ) this.el.setAttribute("id",`xrf-${mesh.name}`)
-
}
},500)
diff --git a/src/3rd/js/three/index.js b/src/3rd/js/three/index.js
index a7b4817..6e594d0 100644
--- a/src/3rd/js/three/index.js
+++ b/src/3rd/js/three/index.js
@@ -72,6 +72,7 @@ xrf.reset = () => {
xrf.audio = []
xrf.add( xrf.interactive )
xrf.layers = 0
+ xrf.emit('reset',{})
}
xrf.parseUrl = (url) => {
diff --git a/src/3rd/js/three/xrf/href.js b/src/3rd/js/three/xrf/href.js
index b7220d6..1014708 100644
--- a/src/3rd/js/three/xrf/href.js
+++ b/src/3rd/js/three/xrf/href.js
@@ -34,12 +34,6 @@ xrf.frag.href = function(v, opts){
if( mesh.userData.XRF.href.exec ) return // mesh already initialized
- const world = {
- pos: new THREE.Vector3(),
- scale: new THREE.Vector3(),
- quat: new THREE.Quaternion()
- }
-
mesh.material = mesh.material.clone() // we need this so we can individually highlight meshes
let click = mesh.userData.XRF.href.exec = (e) => {
@@ -84,12 +78,6 @@ xrf.frag.href = function(v, opts){
// lazy add mesh (because we're inside a recursive traverse)
setTimeout( (mesh) => {
- //mesh.getWorldPosition(world.pos)
- //mesh.getWorldScale(world.scale)
- //mesh.getWorldQuaternion(world.quat);
- //mesh.position.copy(world.pos)
- //mesh.scale.copy(world.scale)
- //mesh.setRotationFromQuaternion(world.quat);
xrf.interactive.add(mesh)
xrf.emit('interactionReady', {mesh,xrf:v,clickHandler: mesh.userData.XRF.href.exec })
}, 0, mesh )