envmapping + bugfixes

This commit is contained in:
Leon van Kammen 2024-07-12 16:19:19 +00:00
parent 0f7f482a3d
commit b6715c1725
16 changed files with 58502 additions and 510 deletions

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Thu Jul 11 01:59:18 PM UTC 2024 * v0.5.1 generated at Fri Jul 12 04:11:54 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2009,6 +2009,7 @@ xrf.reset = () => {
} }
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
} }
@ -3219,6 +3220,38 @@ xrf.addEventListener('dynamicKey', (opts) => {
}) })
}) })
}) })
// switch camera when multiple cameras for url #mycameraname
xrf.addEventListener('navigateLoaded', (opts) => {
// select active camera if any
let {id,match,v} = opts
let envmap = {}
let current = ''
// Recursive function to traverse the graph
function traverseAndSetEnvMap(node, closestAncestorMaterialMap = null) {
// Check if the current node has a material
if (node.isMesh && node.material) {
if (node.material.map && closestAncestorMaterialMap) {
// If the node has a material map, set the closest ancestor material map
node.material.envMap = closestAncestorMaterialMap;
}
}
// Update the closest ancestor's material map
if (node.isMesh && node.material && node.material.map) {
closestAncestorMaterialMap = node.material.map.clone();
closestAncestorMaterialMap.mapping = THREE.EquirectangularReflectionMapping;
closestAncestorMaterialMap.needsUpdate = true
}
// Recursively traverse all children
node.children.forEach(child => traverseAndSetEnvMap(child, closestAncestorMaterialMap));
}
// Start traversal from the root node
traverseAndSetEnvMap(xrf.scene);
})
const doFilter = (opts) => { const doFilter = (opts) => {
let {scene,id,match,v} = opts let {scene,id,match,v} = opts
@ -4117,34 +4150,6 @@ let videoMimeTypes = [
'video/mp4' 'video/mp4'
] ]
videoMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadVideo(mimetype) ) videoMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadVideo(mimetype) )
// poor man's way to move forward using hand gesture pinch
window.AFRAME.registerComponent('envmap', {
schema:{
src: {type: "string"}
},
init: function(){
const loader = new THREE.TextureLoader();
const onLoad = (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
texture.mapping = THREE.EquirectangularReflectionMapping;
texture.needsUpdate = true
xrf.scene.environment = texture
xrf.scene.texture = texture
}
new THREE.TextureLoader().load( this.data.src, onLoad, null, console.error );
xrf.addEventListener('navigateLoaded', () => {
xrf.scene.traverse( (n) => {
if( n.material && n.material.isMeshPhysicalMaterial){
n.material.envMap = xrf.scene.environment
n.material.needsUpdate = true
}
})
})
},
})
window.AFRAME.registerComponent('href', { window.AFRAME.registerComponent('href', {
schema: { schema: {
}, },

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Thu Jul 11 01:59:18 PM UTC 2024 * v0.5.1 generated at Fri Jul 12 04:11:54 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2007,6 +2007,7 @@ xrf.reset = () => {
} }
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
} }
@ -3217,6 +3218,38 @@ xrf.addEventListener('dynamicKey', (opts) => {
}) })
}) })
}) })
// switch camera when multiple cameras for url #mycameraname
xrf.addEventListener('navigateLoaded', (opts) => {
// select active camera if any
let {id,match,v} = opts
let envmap = {}
let current = ''
// Recursive function to traverse the graph
function traverseAndSetEnvMap(node, closestAncestorMaterialMap = null) {
// Check if the current node has a material
if (node.isMesh && node.material) {
if (node.material.map && closestAncestorMaterialMap) {
// If the node has a material map, set the closest ancestor material map
node.material.envMap = closestAncestorMaterialMap;
}
}
// Update the closest ancestor's material map
if (node.isMesh && node.material && node.material.map) {
closestAncestorMaterialMap = node.material.map.clone();
closestAncestorMaterialMap.mapping = THREE.EquirectangularReflectionMapping;
closestAncestorMaterialMap.needsUpdate = true
}
// Recursively traverse all children
node.children.forEach(child => traverseAndSetEnvMap(child, closestAncestorMaterialMap));
}
// Start traversal from the root node
traverseAndSetEnvMap(xrf.scene);
})
const doFilter = (opts) => { const doFilter = (opts) => {
let {scene,id,match,v} = opts let {scene,id,match,v} = opts
@ -4115,34 +4148,6 @@ let videoMimeTypes = [
'video/mp4' 'video/mp4'
] ]
videoMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadVideo(mimetype) ) videoMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadVideo(mimetype) )
// poor man's way to move forward using hand gesture pinch
window.AFRAME.registerComponent('envmap', {
schema:{
src: {type: "string"}
},
init: function(){
const loader = new THREE.TextureLoader();
const onLoad = (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
texture.mapping = THREE.EquirectangularReflectionMapping;
texture.needsUpdate = true
xrf.scene.environment = texture
xrf.scene.texture = texture
}
new THREE.TextureLoader().load( this.data.src, onLoad, null, console.error );
xrf.addEventListener('navigateLoaded', () => {
xrf.scene.traverse( (n) => {
if( n.material && n.material.isMeshPhysicalMaterial){
n.material.envMap = xrf.scene.environment
n.material.needsUpdate = true
}
})
})
},
})
window.AFRAME.registerComponent('href', { window.AFRAME.registerComponent('href', {
schema: { schema: {
}, },

57979
dist/xrfragment.module.js vendored

File diff suppressed because it is too large Load diff

View file

@ -343,7 +343,6 @@ window.accessibility = (opts) => new Proxy({
setupHrefCycling(){ setupHrefCycling(){
// speak arrow keys // speak arrow keys
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {
console.log(e.key)
if( e.key != "Tab" && e.key != "Enter" ) return if( e.key != "Tab" && e.key != "Enter" ) return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last ) let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
if( !subScene ) subScene = xrf.scene if( !subScene ) subScene = xrf.scene

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Thu Jul 11 01:59:18 PM UTC 2024 * v0.5.1 generated at Fri Jul 12 04:11:54 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2007,6 +2007,7 @@ xrf.reset = () => {
} }
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
} }
@ -3217,6 +3218,38 @@ xrf.addEventListener('dynamicKey', (opts) => {
}) })
}) })
}) })
// switch camera when multiple cameras for url #mycameraname
xrf.addEventListener('navigateLoaded', (opts) => {
// select active camera if any
let {id,match,v} = opts
let envmap = {}
let current = ''
// Recursive function to traverse the graph
function traverseAndSetEnvMap(node, closestAncestorMaterialMap = null) {
// Check if the current node has a material
if (node.isMesh && node.material) {
if (node.material.map && closestAncestorMaterialMap) {
// If the node has a material map, set the closest ancestor material map
node.material.envMap = closestAncestorMaterialMap;
}
}
// Update the closest ancestor's material map
if (node.isMesh && node.material && node.material.map) {
closestAncestorMaterialMap = node.material.map.clone();
closestAncestorMaterialMap.mapping = THREE.EquirectangularReflectionMapping;
closestAncestorMaterialMap.needsUpdate = true
}
// Recursively traverse all children
node.children.forEach(child => traverseAndSetEnvMap(child, closestAncestorMaterialMap));
}
// Start traversal from the root node
traverseAndSetEnvMap(xrf.scene);
})
const doFilter = (opts) => { const doFilter = (opts) => {
let {scene,id,match,v} = opts let {scene,id,match,v} = opts

View file

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Thu Jul 11 01:59:18 PM UTC 2024 * v0.5.1 generated at Fri Jul 12 04:11:54 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -2007,6 +2007,7 @@ xrf.reset = () => {
} }
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
} }
@ -3217,6 +3218,38 @@ xrf.addEventListener('dynamicKey', (opts) => {
}) })
}) })
}) })
// switch camera when multiple cameras for url #mycameraname
xrf.addEventListener('navigateLoaded', (opts) => {
// select active camera if any
let {id,match,v} = opts
let envmap = {}
let current = ''
// Recursive function to traverse the graph
function traverseAndSetEnvMap(node, closestAncestorMaterialMap = null) {
// Check if the current node has a material
if (node.isMesh && node.material) {
if (node.material.map && closestAncestorMaterialMap) {
// If the node has a material map, set the closest ancestor material map
node.material.envMap = closestAncestorMaterialMap;
}
}
// Update the closest ancestor's material map
if (node.isMesh && node.material && node.material.map) {
closestAncestorMaterialMap = node.material.map.clone();
closestAncestorMaterialMap.mapping = THREE.EquirectangularReflectionMapping;
closestAncestorMaterialMap.needsUpdate = true
}
// Recursively traverse all children
node.children.forEach(child => traverseAndSetEnvMap(child, closestAncestorMaterialMap));
}
// Start traversal from the root node
traverseAndSetEnvMap(xrf.scene);
})
const doFilter = (opts) => { const doFilter = (opts) => {
let {scene,id,match,v} = opts let {scene,id,match,v} = opts

View file

@ -1249,12 +1249,12 @@ Some pointers for good UX (but not necessary to be XR Fragment compatible):</p>
| | | author.com/article.txt | | | | author.com/article.txt |
| index.gltf | +------------------------+ | index.gltf | +------------------------+
| │ | | | | │ | | |
| ├── ◻ article_canvas | | Hello friends. | | ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | | | │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby | | │ | +------------------------+
| └── ◻ note_canvas | | ... | | └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` | | } | | └ src:`data:welcome human\n@book{sunday...}` |
| | +------------------------+ | |
| | | |
+--------------------------------------------------------------+ +--------------------------------------------------------------+
</code></pre> </code></pre>
@ -1276,84 +1276,95 @@ The XR Fragment-compatible browser can let the enduser access visual-meta(data)-
<li>export: if the 3D scene contains relative src/href values, rewrite them into absolute URL values.</li> <li>export: if the 3D scene contains relative src/href values, rewrite them into absolute URL values.</li>
</ol> </ol>
<h1 id="transclusion-broken-link-resolution">Transclusion (broken link) resolution</h1> <h1 id="reflection-mapping">Reflection Mapping</h1>
<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> <p>Environment mapping is crucial for creating realistic reflections and lighting effects on 3D objects.
To apply environment mapping efficiently in a 3D scene, traverse the scene graph and assign each object&rsquo;s environment map based on the nearest ancestor&rsquo;s texture map. This ensures that objects inherit the correct environment mapping from their closest parent with a texture, enhancing the visual consistency and realism.</p>
<ol> <p>&rdquo;&ldquo;&rdquo;
<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> +&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;+<br>
<li>mirroring files on another protocol using (HTTP) errorcode tags in <code>src</code> or <code>href</code> properties</li> | |<br>
<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> | index.usdz |<br>
</ol> | │ |<br>
| └── ◻ sphere (texture:foo) |
| └ ◻ cube (texture:bar) | envMap = foo
| └ ◻ cylinder | envMap = bar
+&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;+</p>
<blockquote> <pre><code>
<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> Most 3D viewers apply one and the same environment map for various models, however this logic
</blockquote> allows a more natural &amp; automatic strategy for reflection mapping.
<p>For example:</p> # Transclusion (broken link) resolution
<pre><code> +────────────────────────────────────────────────────────+ In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links &amp; minimize broken links in various ways:
1. defining a different transport protocol (https vs ipfs or DAT) in `src` or `href` values can make a difference
2. mirroring files on another protocol using (HTTP) errorcode tags in `src` or `href` properties
3. in case of `src`: nesting a copy of the embedded object in the placeholder object (`embeddedObject`) will not be replaced when the request fails
&gt; 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:
</code></pre>
<p>+────────────────────────────────────────────────────────+
│ │ │ │
│ index.gltf │ │ index.gltf │
│ │ │ │ │ │
│ │ #: #-offlinetext │ │ │ #: #-offlinetext │
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: <a href="http://foo.io/campagne.fbx">http://foo.io/campagne.fbx</a>
│ │ └ href@404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #clienterrortext │ │ │ └ href@400: #clienterrortext │
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not) │ └── ◻ embeddedObject &lt;&mdash;&mdash;&mdash; the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed. │ └ src: <a href="https://foo.io/bar.gltf">https://foo.io/bar.gltf</a> │ 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: <a href="http://foo.io/bar.gltf">http://foo.io/bar.gltf</a> │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed. │ └ src@400: <a href="https://archive.org/l2kj43.gltf">https://archive.org/l2kj43.gltf</a> │ will be displayed.
│ │ │ │
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+</p>
<pre><code>
# 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.&lt;br&gt;
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.&lt;br&gt;
To filter out non-related objects one could take it a step further using filters:
* `href: schoolA.edu/projects.gltf#math&amp;-topics math`
* `href: schoolB.edu/projects.gltf#math&amp;-courses math`
* `href: university.edu/projects.gltf#math&amp;-theme math`
&gt; 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.
# URI Templates (RFC6570)
XR Fragments adopts Level1 URI **Fragment** expansion to provide safe interactivity.&lt;br&gt;
The following demonstrates a simple video player:
</code></pre> </code></pre>
<h1 id="topic-based-index-less-webrings">Topic-based index-less Webrings</h1> <p>+─────────────────────────────────────────────+
<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 filters:</p>
<ul>
<li><code>href: schoolA.edu/projects.gltf#math&amp;-topics math</code></li>
<li><code>href: schoolB.edu/projects.gltf#math&amp;-courses math</code></li>
<li><code>href: university.edu/projects.gltf#math&amp;-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="uri-templates-rfc6570">URI Templates (RFC6570)</h1>
<p>XR Fragments adopts Level1 URI <strong>Fragment</strong> expansion to provide safe interactivity.<br>
The following demonstrates a simple video player:</p>
<pre><code>
+─────────────────────────────────────────────+
│ │ │ │
│ foo.usdz │ │ foo.usdz │<br>
│ │ │ │ │ │<br>
│ │ │ │ │ │<br>
│ ├── ◻ stopbutton │ │ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │ │ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button) │ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button)
│ │ │ │ │ │<br>
│ └── ◻ plane │ │ └── ◻ plane │
│ ├ play: #t=l:0,10 │ │ ├ play: #t=l:0,10 │
│ ├ stop: #t=0,0 │ │ ├ stop: #t=0,0 │
@ -1361,10 +1372,9 @@ The following demonstrates a simple video player:</p>
│ └ src: cat.mp4#{player} │ │ └ src: cat.mp4#{player} │
│ │ │ │
│ │ │ │
+─────────────────────────────────────────────+ +─────────────────────────────────────────────+</p>
<p>&rdquo;`</p>
</code></pre>
<h1 id="additional-scene-metadata">Additional scene metadata</h1> <h1 id="additional-scene-metadata">Additional scene metadata</h1>

View file

@ -763,12 +763,12 @@ For all other purposes, regular mimetypes can be used (but are not required by t
| | | author.com/article.txt | | | | author.com/article.txt |
| index.gltf | +------------------------+ | index.gltf | +------------------------+
| │ | | | | │ | | |
| ├── ◻ article_canvas | | Hello friends. | | ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | | | │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby | | │ | +------------------------+
| └── ◻ note_canvas | | ... | | └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` | | } | | └ src:`data:welcome human\n@book{sunday...}` |
| | +------------------------+ | |
| | | |
+--------------------------------------------------------------+ +--------------------------------------------------------------+
``` ```
@ -786,6 +786,24 @@ For usecases like importing/exporting/p2p casting a scene, the issue of external
1. export: if the 3D scene contains relative src/href values, rewrite them into absolute URL values. 1. export: if the 3D scene contains relative src/href values, rewrite them into absolute URL values.
# Reflection Mapping
Environment mapping is crucial for creating realistic reflections and lighting effects on 3D objects.
To apply environment mapping efficiently in a 3D scene, traverse the scene graph and assign each object's environment map based on the nearest ancestor's texture map. This ensures that objects inherit the correct environment mapping from their closest parent with a texture, enhancing the visual consistency and realism.
``````
+--------------------------------+
| |
| index.usdz |
| │ |
| └── ◻ sphere (texture:foo) |
| └ ◻ cube (texture:bar) | envMap = foo
| └ ◻ cylinder | envMap = bar
+--------------------------------+
```
Most 3D viewers apply one and the same environment map for various models, however this logic
allows a more natural & automatic strategy for reflection mapping.
# Transclusion (broken link) resolution # Transclusion (broken link) resolution

View file

@ -103,9 +103,9 @@ Table of Contents
17.1. Default Data URI mimetype . . . . . . . . . . . . . . . 28 17.1. Default Data URI mimetype . . . . . . . . . . . . . . . 28
17.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 29 17.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 29
18. Importing/exporting . . . . . . . . . . . . . . . . . . . . . 30 18. Importing/exporting . . . . . . . . . . . . . . . . . . . . . 30
19. Transclusion (broken link) resolution . . . . . . . . . . . . 30 19. Reflection Mapping . . . . . . . . . . . . . . . . . . . . . 30
20. Topic-based index-less Webrings . . . . . . . . . . . . . . . 31 20. Additional scene metadata . . . . . . . . . . . . . . . . . . 31
21. URI Templates (RFC6570) . . . . . . . . . . . . . . . . . . . 31 21. Accessibility interface . . . . . . . . . . . . . . . . . . . 33
@ -114,15 +114,13 @@ van Kammen Expires 13 January 2025 [Page 2]
Internet-Draft XR Fragments July 2024 Internet-Draft XR Fragments July 2024
22. Additional scene metadata . . . . . . . . . . . . . . . . . . 32 21.1. Two-button navigation . . . . . . . . . . . . . . . . . 34
23. Accessibility interface . . . . . . . . . . . . . . . . . . . 33 22. Security Considerations . . . . . . . . . . . . . . . . . . . 34
23.1. Two-button navigation . . . . . . . . . . . . . . . . . 34 23. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
24. Security Considerations . . . . . . . . . . . . . . . . . . . 35 24. authors . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
25. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 25. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 35
26. authors . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 26. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 35
27. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 36 27. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 35
28. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 36
29. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 36
1. Introduction 1. Introduction
@ -165,6 +163,8 @@ Internet-Draft XR Fragments July 2024
van Kammen Expires 13 January 2025 [Page 3] van Kammen Expires 13 January 2025 [Page 3]
Internet-Draft XR Fragments July 2024 Internet-Draft XR Fragments July 2024
@ -1595,12 +1595,12 @@ Internet-Draft XR Fragments July 2024
| | | author.com/article.txt | | | | author.com/article.txt |
| index.gltf | +------------------------+ | index.gltf | +------------------------+
| │ | | | | │ | | |
| ├── ◻ article_canvas | | Hello friends. | | ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | | | │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby | | │ | +------------------------+
| └── ◻ note_canvas | | ... | | └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` | | } | | └ src:`data:welcome human\n@book{sunday...}` |
| | +------------------------+ | |
| | | |
+--------------------------------------------------------------+ +--------------------------------------------------------------+
@ -1634,46 +1634,46 @@ Internet-Draft XR Fragments July 2024
1. export: if the 3D scene contains relative src/href values, 1. export: if the 3D scene contains relative src/href values,
rewrite them into absolute URL values. rewrite them into absolute URL values.
19. Transclusion (broken link) resolution 19. Reflection Mapping
In spirit of Ted Nelson's 'transclusion resolution', there's a soft- Environment mapping is crucial for creating realistic reflections and
mechanism to harden links & minimize broken links in various ways: lighting effects on 3D objects. To apply environment mapping
efficiently in a 3D scene, traverse the scene graph and assign each
object's environment map based on the nearest ancestor's texture map.
This ensures that objects inherit the correct environment mapping
from their closest parent with a texture, enhancing the visual
consistency and realism.
1. defining a different transport protocol (https vs ipfs or DAT) in `````` +--------------------------------+
src or href values can make a difference | |
2. mirroring files on another protocol using (HTTP) errorcode tags | index.usdz |
in src or href properties | │ |
3. in case of src: nesting a copy of the embedded object in the | └── ◻ sphere (texture:foo) | | └ ◻ cube (texture:bar) | envMap =
placeholder object (embeddedObject) will not be replaced when the foo | └ ◻ cylinder | envMap = bar +--------------------------------+
request fails
| due to the popularity, maturity and extensiveness of HTTP codes Most 3D viewers apply one and the same environment map for various models, however this logic
| for client/server communication, non-HTTP protocols easily map to allows a more natural & automatic strategy for reflection mapping.
| HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)
# Transclusion (broken link) resolution
In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links & minimize broken links in various ways:
1. defining a different transport protocol (https vs ipfs or DAT) in `src` or `href` values can make a difference
2. mirroring files on another protocol using (HTTP) errorcode tags in `src` or `href` properties
3. in case of `src`: nesting a copy of the embedded object in the placeholder object (`embeddedObject`) will not be replaced when the request fails
> due to the popularity, maturity and extensiveness of HTTP codes for client/server communication, non-HTTP protocols easily map to HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)
For example: For example:
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+ │ │ │
│ │ index.gltf │ │ │ │ │ │ #: #-offlinetext │ │ │ │ │ ├── ◻ buttonA │ │ │
│ index.gltf │ └ href: http://foo.io/campagne.fbx (http://foo.io/campagne.fbx) │ │ │
│ │ │ └ href@404: ipfs://foo.io/campagne.fbx │ │ │ └ href@400:
│ │ #: #-offlinetext │ #clienterrortext │ │ │ └ ◻ offlinetext │ │ │ │ │ └── ◻ embeddedObject
│ │ │ <--------- the meshdata inside embeddedObject will (not) │ └ src:
│ ├── ◻ buttonA │ https://foo.io/bar.gltf (https://foo.io/bar.gltf) │ be flushed when
│ │ └ href: http://foo.io/campagne.fbx │ the request (does not) succeed. │ └ src@404: http://foo.io/bar.gltf
│ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #clienterrortext │
│ │ └ ◻ offlinetext │
│ │ │
│ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
│ └ src@404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ │
+────────────────────────────────────────────────────────+
@ -1682,54 +1682,54 @@ van Kammen Expires 13 January 2025 [Page 30]
Internet-Draft XR Fragments July 2024 Internet-Draft XR Fragments July 2024
20. Topic-based index-less Webrings (http://foo.io/bar.gltf) │ So worstcase the 3D data (of the time of
publishing index.gltf) │ └ src@400: https://archive.org/l2kj43.gltf
(https://archive.org/l2kj43.gltf) │ will be displayed. │ │
+────────────────────────────────────────────────────────+
As hashtags in URLs map to the XWRG, href-values can be used to # Topic-based index-less Webrings
promote topic-based index-less webrings.
Consider 3D scenes linking to eachother using these href values:
* href: schoolA.edu/projects.gltf#math As hashtags in URLs map to the XWRG, `href`-values can be used to promote topic-based index-less webrings.<br>
* href: schoolB.edu/projects.gltf#math Consider 3D scenes linking to eachother using these `href` values:
* href: university.edu/projects.gltf#math
These links would all show visible links to math-tagged objects in * `href: schoolA.edu/projects.gltf#math`
the scene. * `href: schoolB.edu/projects.gltf#math`
To filter out non-related objects one could take it a step further * `href: university.edu/projects.gltf#math`
using filters:
* href: schoolA.edu/projects.gltf#math&-topics math These links would all show visible links to math-tagged objects in the scene.<br>
* href: schoolB.edu/projects.gltf#math&-courses math To filter out non-related objects one could take it a step further using filters:
* href: university.edu/projects.gltf#math&-theme math
| This would hide all object tagged with topic, courses or theme * `href: schoolA.edu/projects.gltf#math&-topics math`
| (including math) so that later only objects tagged with math will * `href: schoolB.edu/projects.gltf#math&-courses math`
| be visible * `href: university.edu/projects.gltf#math&-theme math`
This makes spatial content multi-purpose, without the need to > This would hide all object tagged with `topic`, `courses` or `theme` (including math) so that later only objects tagged with `math` will be visible
separate content into separate files, or show/hide things using a
complex logiclayer like javascript.
21. URI Templates (RFC6570) 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.
XR Fragments adopts Level1 URI *Fragment* expansion to provide safe # URI Templates (RFC6570)
interactivity.
XR Fragments adopts Level1 URI **Fragment** expansion to provide safe interactivity.<br>
The following demonstrates a simple video player: The following demonstrates a simple video player:
+─────────────────────────────────────────────+ │ │ │ foo.usdz │
│ │ │
│ │ │
│ ├── ◻ stopbutton │ │ │ ├ #: #-stopbutton │ │ │ └ href:
#player=stop&-stopbutton │ (stop and hide stop-button) │ │ │
│ └── ◻ plane │ │ ├ play: #t=l:0,10 │ │ ├ stop: #t=0,0 │ │ ├ href:
#player=play&stopbutton │ (play and show stop-button) │ └ src:
cat.mp4#{player} │ │ │ │ │
+─────────────────────────────────────────────+
```
20. Additional scene metadata
XR Fragments does not aim to redefine the metadata-space or
accessibility-space by introducing its own cataloging-metadata
fields. Instead, it encourages browsers to scan nodes for the
following custom properties:
@ -1738,31 +1738,6 @@ van Kammen Expires 13 January 2025 [Page 31]
Internet-Draft XR Fragments July 2024 Internet-Draft XR Fragments July 2024
+─────────────────────────────────────────────+
│ │
│ foo.usdz │
│ │ │
│ │ │
│ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&-stopbutton │ (stop and hide stop-button)
│ │ │
│ └── ◻ plane │
│ ├ play: #t=l:0,10 │
│ ├ stop: #t=0,0 │
│ ├ href: #player=play&stopbutton │ (play and show stop-button)
│ └ src: cat.mp4#{player} │
│ │
│ │
+─────────────────────────────────────────────+
22. Additional scene metadata
XR Fragments does not aim to redefine the metadata-space or
accessibility-space by introducing its own cataloging-metadata
fields. Instead, it encourages browsers to scan nodes for the
following custom properties:
* SPDX (https://spdx.dev/) license information * SPDX (https://spdx.dev/) license information
* ARIA (https://www.w3.org/WAI/standards-guidelines/aria/) * ARIA (https://www.w3.org/WAI/standards-guidelines/aria/)
attributes (aria-*: .....) attributes (aria-*: .....)
@ -1785,15 +1760,6 @@ Internet-Draft XR Fragments July 2024
Individual nodes can be enriched with such metadata, but most Individual nodes can be enriched with such metadata, but most
importantly the scene node: importantly the scene node:
van Kammen Expires 13 January 2025 [Page 32]
Internet-Draft XR Fragments July 2024
+================================+=========================+ +================================+=========================+
| metadata key | example value | | metadata key | example value |
+================================+=========================+ +================================+=========================+
@ -1821,13 +1787,20 @@ Internet-Draft XR Fragments July 2024
| * = these are interchangable (only one needs to be defined) | * = these are interchangable (only one needs to be defined)
van Kammen Expires 13 January 2025 [Page 32]
Internet-Draft XR Fragments July 2024
There's no silver bullet when it comes to metadata, so one should There's no silver bullet when it comes to metadata, so one should
support where the metadata is/goes. support where the metadata is/goes.
| These attributes can be scanned and presented during an href or | These attributes can be scanned and presented during an href or
| src eye/mouse-over. | src eye/mouse-over.
23. Accessibility interface 21. Accessibility interface
The addressibility of XR Fragments allows for unique 3D-to-text The addressibility of XR Fragments allows for unique 3D-to-text
transcripts, as well as an textual interface to navigate 3D content. transcripts, as well as an textual interface to navigate 3D content.
@ -1842,14 +1815,6 @@ Internet-Draft XR Fragments July 2024
to read (via screenreader, screen, or TTS e.g.) to read (via screenreader, screen, or TTS e.g.)
4. the textlog contains aria-descriptions, and its narration 4. the textlog contains aria-descriptions, and its narration
(Screenreader e.g.) can be skipped (via 2-button navigation) (Screenreader e.g.) can be skipped (via 2-button navigation)
van Kammen Expires 13 January 2025 [Page 33]
Internet-Draft XR Fragments July 2024
5. The back command should navigate back to the previous URL (alias 5. The back command should navigate back to the previous URL (alias
for browser-backbutton) for browser-backbutton)
6. The forward command should navigate back to the next URL (alias 6. The forward command should navigate back to the next URL (alias
@ -1877,7 +1842,15 @@ Internet-Draft XR Fragments July 2024
https://.../... in case a 3D node exist with name abc and href https://.../... in case a 3D node exist with name abc and href
value https://.../... value https://.../...
23.1. Two-button navigation
van Kammen Expires 13 January 2025 [Page 33]
Internet-Draft XR Fragments July 2024
21.1. Two-button navigation
For specific user-profiles, gyroscope/mouse/keyboard/audio/visuals For specific user-profiles, gyroscope/mouse/keyboard/audio/visuals
will not be available. will not be available.
@ -1891,22 +1864,7 @@ Internet-Draft XR Fragments July 2024
3. the TTS reads the href-value (and/or aria-description if 3. the TTS reads the href-value (and/or aria-description if
available) available)
22. Security Considerations
van Kammen Expires 13 January 2025 [Page 34]
Internet-Draft XR Fragments July 2024
24. Security Considerations
The only dynamic parts are W3C Media Fragments The only dynamic parts are W3C Media Fragments
(https://www.w3.org/TR/media-frags/) and URI Templates (RFC6570) (https://www.w3.org/TR/media-frags/) and URI Templates (RFC6570)
@ -1916,7 +1874,7 @@ Internet-Draft XR Fragments July 2024
In fact, it is much safer than relying on a scripting language In fact, it is much safer than relying on a scripting language
(javascript) which can change URN too. (javascript) which can change URN too.
25. FAQ 23. 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
@ -1940,6 +1898,14 @@ Internet-Draft XR Fragments July 2024
scripting language. scripting language.
XR Fragments supports filtering objects in a scene only, because in XR Fragments supports filtering objects in a scene only, because in
the history of the javascript-powered web, showing/hiding document- the history of the javascript-powered web, showing/hiding document-
van Kammen Expires 13 January 2025 [Page 34]
Internet-Draft XR Fragments July 2024
entities seems to be one of the most popular basic usecases. entities seems to be one of the most popular basic usecases.
Doing advanced scripting & networkrequests under the hood are Doing advanced scripting & networkrequests under the hood are
obviously interesting endavours, but this is something which should obviously interesting endavours, but this is something which should
@ -1949,24 +1915,16 @@ Internet-Draft XR Fragments July 2024
place, to 'extend' experiences, in contrast to code/javascript inside place, to 'extend' experiences, in contrast to code/javascript inside
hypermedia documents (this turned out as a hypermedia antipattern). hypermedia documents (this turned out as a hypermedia antipattern).
26. authors 24. authors
* Leon van Kammen (@lvk@mastodon.online) * Leon van Kammen (@lvk@mastodon.online)
* Jens Finkhäuser (@jens@social.finkhaeuser.de) * Jens Finkhäuser (@jens@social.finkhaeuser.de)
25. IANA Considerations
van Kammen Expires 13 January 2025 [Page 35]
Internet-Draft XR Fragments July 2024
27. IANA Considerations
This document has no IANA actions. This document has no IANA actions.
28. Acknowledgments 26. Acknowledgments
* NLNET (https://nlnet.nl) * NLNET (https://nlnet.nl)
* Future of Text (https://futureoftext.org) * Future of Text (https://futureoftext.org)
@ -1981,7 +1939,7 @@ Internet-Draft XR Fragments July 2024
* Brandel Zackernuk * Brandel Zackernuk
* Mark Anderson * Mark Anderson
29. Appendix: Definitions 27. Appendix: Definitions
+=================+=============================================+ +=================+=============================================+
| definition | explanation | | definition | explanation |
@ -1996,6 +1954,14 @@ Internet-Draft XR Fragments July 2024
| 3D object | an object inside a scene characterized by | | 3D object | an object inside a scene characterized by |
| | vertex-, face- and customproperty data. | | | vertex-, face- and customproperty data. |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
van Kammen Expires 13 January 2025 [Page 35]
Internet-Draft XR Fragments July 2024
| URI | some resource at something somewhere via | | URI | some resource at something somewhere via |
| | someprotocol (http://me.com/foo.glb#foo or | | | someprotocol (http://me.com/foo.glb#foo or |
| | e76f8efec8efce98e6f see interpeer.io | | | e76f8efec8efce98e6f see interpeer.io |
@ -2010,14 +1976,6 @@ Internet-Draft XR Fragments July 2024
| | Object(nodes), relevant to machines and a | | | Object(nodes), relevant to machines and a |
| | human minority (academics/developers) | | | human minority (academics/developers) |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
van Kammen Expires 13 January 2025 [Page 36]
Internet-Draft XR Fragments July 2024
| XR fragment | URI Fragment with spatial hints like | | XR fragment | URI Fragment with spatial hints like |
| | #pos=0,0,0&t=1,100 e.g. | | | #pos=0,0,0&t=1,100 e.g. |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
@ -2052,6 +2010,14 @@ Internet-Draft XR Fragments July 2024
| | indirectly visible/editable in XR. | | | indirectly visible/editable in XR. |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
| requestless | metadata which never spawns new requests | | requestless | metadata which never spawns new requests |
van Kammen Expires 13 January 2025 [Page 36]
Internet-Draft XR Fragments July 2024
| metadata | (unlike RDF/HTML, which can cause | | metadata | (unlike RDF/HTML, which can cause |
| | framerate-dropping, hence not used a lot in | | | framerate-dropping, hence not used a lot in |
| | games) | | | games) |
@ -2066,14 +2032,6 @@ Internet-Draft XR Fragments July 2024
| extrospective | outward sensemaking ("I'm fairly sure John | | extrospective | outward sensemaking ("I'm fairly sure John |
| | is a person who lives in oklahoma") | | | is a person who lives in oklahoma") |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
van Kammen Expires 13 January 2025 [Page 37]
Internet-Draft XR Fragments July 2024
| ◻ | ascii representation of an 3D object/mesh | | ◻ | ascii representation of an 3D object/mesh |
+-----------------+---------------------------------------------+ +-----------------+---------------------------------------------+
| (un)obtrusive | obtrusive: wrapping human text/thought in | | (un)obtrusive | obtrusive: wrapping human text/thought in |
@ -2111,18 +2069,4 @@ Internet-Draft XR Fragments July 2024
van Kammen Expires 13 January 2025 [Page 37]
van Kammen Expires 13 January 2025 [Page 38]

View file

@ -1105,12 +1105,12 @@ Some pointers for good UX (but not necessary to be XR Fragment compatible):</t>
| | | author.com/article.txt | | | | author.com/article.txt |
| index.gltf | +------------------------+ | index.gltf | +------------------------+
| │ | | | | │ | | |
| ├── ◻ article_canvas | | Hello friends. | | ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | | | │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby | | │ | +------------------------+
| └── ◻ note_canvas | | ... | | └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` | | } | | └ src:`data:welcome human\n@book{sunday...}` |
| | +------------------------+ | |
| | | |
+--------------------------------------------------------------+ +--------------------------------------------------------------+
]]> ]]>
@ -1131,90 +1131,102 @@ The XR Fragment-compatible browser can let the enduser access visual-meta(data)-
</ol> </ol>
</section> </section>
<section anchor="transclusion-broken-link-resolution"><name>Transclusion (broken link) resolution</name> <section anchor="reflection-mapping"><name>Reflection Mapping</name>
<t>In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links &amp; minimize broken links in various ways:</t> <t>Environment mapping is crucial for creating realistic reflections and lighting effects on 3D objects.
To apply environment mapping efficiently in a 3D scene, traverse the scene graph and assign each object's environment map based on the nearest ancestor's texture map. This ensures that objects inherit the correct environment mapping from their closest parent with a texture, enhancing the visual consistency and realism.</t>
<t>``````
+--------------------------------+<br />
| |<br />
| index.usdz |<br />
| │ |<br />
| └── ◻ sphere (texture:foo) |
| └ ◻ cube (texture:bar) | envMap = foo
| └ ◻ cylinder | envMap = bar
+--------------------------------+</t>
<ol spacing="compact"> <artwork><![CDATA[
<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> Most 3D viewers apply one and the same environment map for various models, however this logic
<li>mirroring files on another protocol using (HTTP) errorcode tags in <tt>src</tt> or <tt>href</tt> properties</li> allows a more natural & automatic strategy for reflection mapping.
<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>
<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><![CDATA[ +────────────────────────────────────────────────────────+ # Transclusion (broken link) resolution
In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links & minimize broken links in various ways:
1. defining a different transport protocol (https vs ipfs or DAT) in `src` or `href` values can make a difference
2. mirroring files on another protocol using (HTTP) errorcode tags in `src` or `href` properties
3. in case of `src`: nesting a copy of the embedded object in the placeholder object (`embeddedObject`) will not be replaced when the request fails
> due to the popularity, maturity and extensiveness of HTTP codes for client/server communication, non-HTTP protocols easily map to HTTP codes (ipfs ERR_NOT_FOUND maps to 404 e.g.)
For example:
]]>
</artwork>
<t>+────────────────────────────────────────────────────────+
│ │ │ │
│ index.gltf │ │ index.gltf │
│ │ │ │ │ │
│ │ #: #-offlinetext │ │ │ #: #-offlinetext │
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: <eref target="http://foo.io/campagne.fbx">http://foo.io/campagne.fbx</eref>
│ │ └ href@404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #clienterrortext │ │ │ └ href@400: #clienterrortext │
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject <--------- 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: <eref target="https://foo.io/bar.gltf">https://foo.io/bar.gltf</eref> │ 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: <eref target="http://foo.io/bar.gltf">http://foo.io/bar.gltf</eref> │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed. │ └ src@400: <eref target="https://archive.org/l2kj43.gltf">https://archive.org/l2kj43.gltf</eref> │ will be displayed.
│ │ │ │
+────────────────────────────────────────────────────────+ +────────────────────────────────────────────────────────+</t>
<artwork><![CDATA[
# 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.<br>
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.<br>
To filter out non-related objects one could take it a step further using filters:
* `href: schoolA.edu/projects.gltf#math&-topics math`
* `href: schoolB.edu/projects.gltf#math&-courses math`
* `href: university.edu/projects.gltf#math&-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.
# URI Templates (RFC6570)
XR Fragments adopts Level1 URI **Fragment** expansion to provide safe interactivity.<br>
The following demonstrates a simple video player:
]]> ]]>
</artwork> </artwork>
</section> <t>+─────────────────────────────────────────────+
<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 filters:</t>
<ul spacing="compact">
<li><tt>href: schoolA.edu/projects.gltf#math&amp;-topics math</tt></li>
<li><tt>href: schoolB.edu/projects.gltf#math&amp;-courses math</tt></li>
<li><tt>href: university.edu/projects.gltf#math&amp;-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="uri-templates-rfc6570"><name>URI Templates (RFC6570)</name>
<t>XR Fragments adopts Level1 URI <strong>Fragment</strong> expansion to provide safe interactivity.<br />
The following demonstrates a simple video player:</t>
<artwork><![CDATA[
+─────────────────────────────────────────────+
│ │ │ │
│ foo.usdz │ │ foo.usdz │<br />
│ │ │ │ │ │<br />
│ │ │ │ │ │<br />
│ ├── ◻ stopbutton │ │ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │ │ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&-stopbutton │ (stop and hide stop-button) │ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button)
│ │ │ │ │ │<br />
│ └── ◻ plane │ │ └── ◻ plane │
│ ├ play: #t=l:0,10 │ │ ├ play: #t=l:0,10 │
│ ├ stop: #t=0,0 │ │ ├ stop: #t=0,0 │
│ ├ href: #player=play&stopbutton │ (play and show stop-button) │ ├ href: #player=play&amp;stopbutton │ (play and show stop-button)
│ └ src: cat.mp4#{player} │ │ └ src: cat.mp4#{player} │
│ │ │ │
│ │ │ │
+─────────────────────────────────────────────+ +─────────────────────────────────────────────+</t>
<t>```</t>
]]>
</artwork>
</section> </section>
<section anchor="additional-scene-metadata"><name>Additional scene metadata</name> <section anchor="additional-scene-metadata"><name>Additional scene metadata</name>

View file

@ -17,7 +17,6 @@
</head> </head>
<body> <body>
<a-scene xr-mode-ui="XRMode: xr" <a-scene xr-mode-ui="XRMode: xr"
envmap="src: https://coderofsalvation.github.io/xrfragment.media/images/sky.jpg"
renderer="colorManagement: false; antialias:true; highRefreshRate:true; foveationLevel: 0.5; toneMapping: ACESFilmic; exposure: 3.0" renderer="colorManagement: false; antialias:true; highRefreshRate:true; foveationLevel: 0.5; toneMapping: ACESFilmic; exposure: 3.0"
device-orientation-permission-ui device-orientation-permission-ui
light="defaultLightsEnabled: false"> light="defaultLightsEnabled: false">

Binary file not shown.

View file

@ -9,16 +9,16 @@
<body style="overflow:hidden; padding:10%"> <body style="overflow:hidden; padding:10%">
<h1>&lt;model-viewer&gt; example</h1> <h1>&lt;model-viewer&gt; example</h1>
<br><br>
<div style="width:90%; height:70vh">
<model-viewer <model-viewer
src="./../assets/index.glb" src="./../assets/index.glb"
environment-image="https://cdn.glitch.global/8e507517-31ff-4aa5-80c1-10ea6de9483d/white_furnace.hdr" environment-image="https://cdn.glitch.global/8e507517-31ff-4aa5-80c1-10ea6de9483d/white_furnace.hdr"
ar alt="XR Fragments demo scene" camera-controls touch-action="none" camera-orbit="-8.142746deg 68.967deg 0.6179899m" camera-target="-0.003m 0.0722m 0.0391m" field-of-view="45deg" min-field-of-view="25deg" max-field-of-view="45deg" interpolation-decay="200" min-camera-orbit="auto auto 5%" ar alt="XR Fragments demo scene" touch-action="none" disable-pan disable-zoom
style="width:100%; height:100%; border-radius:5px; border:1px solid #CCC" field-of-view="75deg" min-field-of-view="25deg" max-field-of-view="100deg"
style="width:80%; height:50vh; border-radius:5px; border:1px solid #CCC"
> >
</model-viewer> </model-viewer>
</div>
<script type="importmap"> <script type="importmap">
{ {
@ -36,7 +36,7 @@
import { USDZLoader } from 'three/addons/loaders/USDZLoader.js'; import { USDZLoader } from 'three/addons/loaders/USDZLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
const mv = document.querySelector("model-viewer"); const mv = window.mv = document.querySelector("model-viewer");
const $url = document.querySelector('#url') const $url = document.querySelector('#url')
const orbitDefault = '50deg 90deg 1.0m' const orbitDefault = '50deg 90deg 1.0m'
@ -52,34 +52,6 @@
} while ((obj = Object.getPrototypeOf(obj))); } while ((obj = Object.getPrototypeOf(obj)));
} }
const setupCSS = (scene) => {
if( document.querySelector('#viewbutton-css') ) return
let style = document.createElement('style')
style.type = 'text/css'
style.innerHTML = `
.view-button {
background: #fff;
border-radius: 4px;
border: none;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
color: rgba(0, 0, 0, 0.8);
display: block;
font-family: Futura, Helvetica Neue, sans-serif;
font-size: 12px;
font-weight: 700;
max-width: 128px;
overflow-wrap: break-word;
padding: 0.5em 1em;
position: absolute;
width: max-content;
height: max-content;
transform: translate3d(-50%, -50%, 0);
}
`
document.body.appendChild(style)
}
const createHotspot = (pos, n) => { const createHotspot = (pos, n) => {
//.name, `${n.userData['aria-description'] || n.name}` ) //.name, `${n.userData['aria-description'] || n.name}` )
const btn = document.createElement('button') const btn = document.createElement('button')
@ -95,6 +67,10 @@
btn.setAttribute('data-position',`${pos.x}m ${pos.y}m ${pos.z}m`) btn.setAttribute('data-position',`${pos.x}m ${pos.y}m ${pos.z}m`)
btn.setAttribute('data-target',`${pos.x}m ${pos.y}m ${pos.z}m`) btn.setAttribute('data-target',`${pos.x}m ${pos.y}m ${pos.z}m`)
btn.setAttribute('data-orbit', orbitDefault ) btn.setAttribute('data-orbit', orbitDefault )
// btn.style.opacity = 0.01
btn.style.borderRadius = '50%'
btn.style.padding = '30px'
btn.style.cursor = 'pointer'
mv.appendChild(btn) mv.appendChild(btn)
} }
@ -106,21 +82,7 @@
createHotspot(pos, n) createHotspot(pos, n)
} }
}) })
setupClicks()
}
const setupDefaultProjection = () => {
xrf.addEventListener('navigateLoaded', () => {
let frag = xrf.URI.parse( xrf.scene.children[0].userData['#'] )
if( frag.XRF.pos ){
let obj = xrf.scene.getObjectByName( frag.XRF.pos.string )
if( !obj ) return console.error('obj '+frag.XRF.pos.string+" not found")
const pos = obj.position
mv.cameraTarget = `${pos.x} ${pos.y} ${pos.z}`
mv.cameraOrbit = orbitDefault;
mv.fieldOfView = '45deg';
}
})
} }
const setupClicks = () => { const setupClicks = () => {
@ -132,8 +94,9 @@
mv.cameraOrbit = dataset.orbit; mv.cameraOrbit = dataset.orbit;
mv.fieldOfView = '45deg'; mv.fieldOfView = '45deg';
console.dir(node) console.dir(node)
if( node && node.userData.XRF && node.userData.XRF.href ){ if( node && node.userData.href ){
node.userData.XRF.href.exec({type:'click'}) // xrf.navigator.to( node.userData.href )
//node.userData.XRF.href.exec({type:'click'})
console.log("clicked!") console.log("clicked!")
} }
} }
@ -142,23 +105,48 @@
}); });
} }
const clear = (obj) => { const setupDefaultProjection = () => {
while(obj.children.length > 0){ let frag = xrf.URI.parse( xrf.scene.children[0].userData['#'] )
clear(obj.children[0]); if( frag.XRF.pos ){
obj.remove(obj.children[0]); let obj = xrf.scene.getObjectByName( frag.XRF.pos.string )
if( !obj ) return console.error('obj '+frag.XRF.pos.string+" not found")
console.log("updating cam")
const pos = new THREE.Vector3()
obj.getWorldPosition(pos)
obj.position.y += 1.6 // add person length
mv.cameraTarget = `${pos.x} ${pos.y} ${pos.z}`
console.log(`${pos.x} ${pos.y} ${pos.z}`)
mv.cameraOrbit = orbitDefault;
mv.fieldOfView = '45deg';
}
} }
if(obj.geometry) obj.geometry.dispose();
if(obj.material){ const setupCSS = (scene) => {
//in case of map, bumpMap, normalMap, envMap ... //if( document.querySelector('#viewbutton-css') ) return
Object.keys(obj.material).forEach(prop => { //let style = document.createElement('style')
if(!obj.material[prop]) //style.type = 'text/css'
return; //style.innerHTML = `
if(obj.material[prop] !== null && typeof obj.material[prop].dispose === 'function') // .view-button {
obj.material[prop].dispose(); // background: #fff;
}) // border-radius: 4px;
obj.material.dispose(); // border: none;
} // box-sizing: border-box;
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
// color: rgba(0, 0, 0, 0.8);
// display: block;
// font-family: Futura, Helvetica Neue, sans-serif;
// font-size: 12px;
// font-weight: 700;
// max-width: 128px;
// overflow-wrap: break-word;
// padding: 0.5em 1em;
// position: absolute;
// width: max-content;
// height: max-content;
// transform: translate3d(-50%, -50%, 0);
// }
//`
//document.body.appendChild(style)
} }
const opts = { const opts = {
@ -171,10 +159,9 @@
const renderer = mv[getSymbol("renderer")].threeRenderer const renderer = mv[getSymbol("renderer")].threeRenderer
const controls = mv[getSymbol("controls")] const controls = mv[getSymbol("controls")]
const camera = mv[getSymbol("scene")].getCamera() const camera = mv[getSymbol("scene")].getCamera()
const url = mv.src
clear(scene); opts = window.opts = {
opts = {
...opts, ...opts,
scene, scene,
renderer, renderer,
@ -185,34 +172,31 @@
window.opts = opts window.opts = opts
if( camera.parent == null ) scene.add(camera) // xr fragments expects in-scene camera // mark current loaded scene for deletion by xrfragment library (except camera)
//scene.traverse( (o) => o.isXRF = o.id != camera.id )
// enable XR fragments // enable XR fragments
if( camera.parent == null ) scene.add(camera) // xr fragments expects in-scene camera
let xrf = opts.xrf.init(opts) let xrf = opts.xrf.init(opts)
window.xrf = xrf window.xrf = xrf
// xrf.sceneRoot.children.map( (c) => (camera.id != c.id) && (c.visible = false) )
xrf.addEventListener('href', (opts) => { xrf.addEventListener('href', (opts) => {
console.log("href!")
console.dir(opts) console.dir(opts)
}) })
//
let url = mv.src
setupDefaultProjection(xrf)
// $url.value = url
// mark current loaded scene for deletion by xrfragment library (except camera)
// now we re-insert the model via the XR Fragments lib (so it will parse the XRF metadata)
xrf.navigator.URI = xrf.URI.parse(document.location.href)
xrf.navigator.to(url)
//if( xrf.URI.isRelative( xrf.URI.parse(url) ) ){
// xrf.navigator.URI = xrf.URI.parse( xrf.navigator.URI.URN + url )
//}
//xrf.loadModel({...scene, scene},url,true)
//setupCSS() xrf.addEventListener('navigateLoaded', function(opts){
//setupHotspots(scene)
//setupClicks() console.dir(xrf.scene)
setupCSS()
setupHotspots(scene)
// setupDefaultProjection()
console.log("ready") console.log("ready")
})
// now we re-insert the model via the XR Fragments lib (so it will parse the XRF metadata)
xrf.navigator.to(url)
} }
mv.addEventListener("load", onLoad(opts) ) mv.addEventListener("load", onLoad(opts) )

View file

@ -1,28 +0,0 @@
// poor man's way to move forward using hand gesture pinch
window.AFRAME.registerComponent('envmap', {
schema:{
src: {type: "string"}
},
init: function(){
const loader = new THREE.TextureLoader();
const onLoad = (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
texture.mapping = THREE.EquirectangularReflectionMapping;
texture.needsUpdate = true
xrf.scene.environment = texture
xrf.scene.texture = texture
}
new THREE.TextureLoader().load( this.data.src, onLoad, null, console.error );
xrf.addEventListener('navigateLoaded', () => {
xrf.scene.traverse( (n) => {
if( n.material && n.material.isMeshPhysicalMaterial){
n.material.envMap = xrf.scene.environment
n.material.needsUpdate = true
}
})
})
},
})

View file

@ -91,7 +91,6 @@ window.accessibility = (opts) => new Proxy({
setupHrefCycling(){ setupHrefCycling(){
// speak arrow keys // speak arrow keys
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {
console.log(e.key)
if( e.key != "Tab" && e.key != "Enter" ) return if( e.key != "Tab" && e.key != "Enter" ) return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last ) let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
if( !subScene ) subScene = xrf.scene if( !subScene ) subScene = xrf.scene

View file

@ -53,7 +53,6 @@ xrf.loadModel = function(model,url,noadd){
let {directory,file,fragment,fileExt} = URI; let {directory,file,fragment,fileExt} = URI;
model.file = URI.file model.file = URI.file
xrf.model = model xrf.model = model
xrf.scene = model.scene
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
@ -124,6 +123,7 @@ xrf.reset = () => {
} }
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
} }