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
* SPDX-License-Identifier: MPL-2.0
*/
@ -1955,7 +1955,7 @@ xrf.loadModel = function(model,url,noadd){
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
@ -2009,6 +2009,7 @@ xrf.reset = () => {
}
xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene
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) => {
let {scene,id,match,v} = opts
@ -4117,34 +4150,6 @@ let videoMimeTypes = [
'video/mp4'
]
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', {
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
* SPDX-License-Identifier: MPL-2.0
*/
@ -1953,7 +1953,7 @@ xrf.loadModel = function(model,url,noadd){
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
@ -2007,6 +2007,7 @@ xrf.reset = () => {
}
xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene
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) => {
let {scene,id,match,v} = opts
@ -4115,34 +4148,6 @@ let videoMimeTypes = [
'video/mp4'
]
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', {
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(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
console.log(e.key)
if( e.key != "Tab" && e.key != "Enter" ) return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
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
* SPDX-License-Identifier: MPL-2.0
*/
@ -1953,7 +1953,7 @@ xrf.loadModel = function(model,url,noadd){
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
@ -2007,6 +2007,7 @@ xrf.reset = () => {
}
xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene
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) => {
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
* SPDX-License-Identifier: MPL-2.0
*/
@ -1953,7 +1953,7 @@ xrf.loadModel = function(model,url,noadd){
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
@ -2007,6 +2007,7 @@ xrf.reset = () => {
}
xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene
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) => {
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 |
| index.gltf | +------------------------+
| │ | | |
| ├── ◻ article_canvas | | Hello friends. |
| ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby |
| └── ◻ note_canvas | | ... |
| └ src:`data:welcome human\n@book{sunday...}` | | } |
| | +------------------------+
| │ | +------------------------+
| └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` |
| |
| |
+--------------------------------------------------------------+
</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>
</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>
<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 (HTTP) errorcode tags in <code>src</code> or <code>href</code> properties</li>
<li>in case of <code>src</code>: nesting a copy of the embedded object in the placeholder object (<code>embeddedObject</code>) will not be replaced when the request fails</li>
</ol>
<p>&rdquo;&ldquo;&rdquo;
+&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;+<br>
| |<br>
| index.usdz |<br>
| │ |<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>
<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>
<pre><code>
Most 3D viewers apply one and the same environment map for various models, however this logic
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 │
│ │ │
│ │ #: #-offlinetext │
│ │ │
│ ├── ◻ 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@400: #clienterrortext │
│ │ └ ◻ offlinetext │
│ │ │
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
│ └ src@404: http://foo.io/bar.gltf │ So worstcase the 3D data (of the time of publishing index.gltf)
│ └ src@400: https://archive.org/l2kj43.gltf │ will be displayed.
│ └── ◻ embeddedObject &lt;&mdash;&mdash;&mdash; the meshdata inside embeddedObject will (not)
│ └ src: <a href="https://foo.io/bar.gltf">https://foo.io/bar.gltf</a> │ be flushed when the request (does not) succeed.
│ └ 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: <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>
<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 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>
+─────────────────────────────────────────────+
<p>+─────────────────────────────────────────────+
│ │
│ foo.usdz │
│ │ │
│ │ │
│ foo.usdz │<br>
│ │ │<br>
│ │ │<br>
│ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button)
│ │ │
│ │ │<br>
│ └── ◻ plane │
│ ├ play: #t=l:0,10 │
│ ├ stop: #t=0,0 │
@ -1361,10 +1372,9 @@ The following demonstrates a simple video player:</p>
│ └ src: cat.mp4#{player} │
│ │
│ │
+─────────────────────────────────────────────+
+─────────────────────────────────────────────+</p>
</code></pre>
<p>&rdquo;`</p>
<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 |
| index.gltf | +------------------------+
| │ | | |
| ├── ◻ article_canvas | | Hello friends. |
| ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby |
| └── ◻ note_canvas | | ... |
| └ src:`data:welcome human\n@book{sunday...}` | | } |
| | +------------------------+
| │ | +------------------------+
| └── ◻ note_canvas |
| └ 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.
# 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

View File

@ -103,9 +103,9 @@ Table of Contents
17.1. Default Data URI mimetype . . . . . . . . . . . . . . . 28
17.2. URL and Data URI . . . . . . . . . . . . . . . . . . . . 29
18. Importing/exporting . . . . . . . . . . . . . . . . . . . . . 30
19. Transclusion (broken link) resolution . . . . . . . . . . . . 30
20. Topic-based index-less Webrings . . . . . . . . . . . . . . . 31
21. URI Templates (RFC6570) . . . . . . . . . . . . . . . . . . . 31
19. Reflection Mapping . . . . . . . . . . . . . . . . . . . . . 30
20. Additional scene metadata . . . . . . . . . . . . . . . . . . 31
21. Accessibility interface . . . . . . . . . . . . . . . . . . . 33
@ -114,15 +114,13 @@ van Kammen Expires 13 January 2025 [Page 2]
Internet-Draft XR Fragments July 2024
22. Additional scene metadata . . . . . . . . . . . . . . . . . . 32
23. Accessibility interface . . . . . . . . . . . . . . . . . . . 33
23.1. Two-button navigation . . . . . . . . . . . . . . . . . 34
24. Security Considerations . . . . . . . . . . . . . . . . . . . 35
25. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
26. authors . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
27. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 36
28. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 36
29. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 36
21.1. Two-button navigation . . . . . . . . . . . . . . . . . 34
22. Security Considerations . . . . . . . . . . . . . . . . . . . 34
23. FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
24. authors . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
25. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 35
26. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 35
27. Appendix: Definitions . . . . . . . . . . . . . . . . . . . . 35
1. Introduction
@ -165,6 +163,8 @@ Internet-Draft XR Fragments July 2024
van Kammen Expires 13 January 2025 [Page 3]
Internet-Draft XR Fragments July 2024
@ -1595,12 +1595,12 @@ Internet-Draft XR Fragments July 2024
| | | author.com/article.txt |
| index.gltf | +------------------------+
| │ | | |
| ├── ◻ article_canvas | | Hello friends. |
| ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby |
| └── ◻ note_canvas | | ... |
| └ src:`data:welcome human\n@book{sunday...}` | | } |
| | +------------------------+
| │ | +------------------------+
| └── ◻ note_canvas |
| └ 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,
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-
mechanism to harden links & minimize broken links in various ways:
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.
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
`````` +--------------------------------+
| |
| index.usdz |
| │ |
| └── ◻ sphere (texture:foo) | | └ ◻ cube (texture:bar) | envMap =
foo | └ ◻ cylinder | envMap = bar +--------------------------------+
| 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.)
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.
For example:
# Transclusion (broken link) resolution
+────────────────────────────────────────────────────────+
│ │
│ index.gltf │
│ │ │
│ │ #: #-offlinetext │
│ │ │
│ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │
│ │ └ 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.
│ │
+────────────────────────────────────────────────────────+
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:
+────────────────────────────────────────────────────────+ │ │ │
index.gltf │ │ │ │ │ │ #: #-offlinetext │ │ │ │ │ ├── ◻ buttonA │ │ │
└ href: http://foo.io/campagne.fbx (http://foo.io/campagne.fbx) │ │ │
└ href@404: ipfs://foo.io/campagne.fbx │ │ │ └ href@400:
#clienterrortext │ │ │ └ ◻ offlinetext │ │ │ │ │ └── ◻ embeddedObject
<--------- the meshdata inside embeddedObject will (not) │ └ src:
https://foo.io/bar.gltf (https://foo.io/bar.gltf) │ be flushed when
the request (does not) succeed. │ └ src@404: http://foo.io/bar.gltf
@ -1682,54 +1682,54 @@ van Kammen Expires 13 January 2025 [Page 30]
Internet-Draft XR Fragments July 2024
20. 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 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.
21. URI Templates (RFC6570)
XR Fragments adopts Level1 URI *Fragment* expansion to provide safe
interactivity.
The following demonstrates a simple video player:
(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. │ │
+────────────────────────────────────────────────────────+
# 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:
+─────────────────────────────────────────────+ │ │ │ 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
+─────────────────────────────────────────────+
│ │
│ 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
* ARIA (https://www.w3.org/WAI/standards-guidelines/aria/)
attributes (aria-*: .....)
@ -1785,15 +1760,6 @@ Internet-Draft XR Fragments July 2024
Individual nodes can be enriched with such metadata, but most
importantly the scene node:
van Kammen Expires 13 January 2025 [Page 32]
Internet-Draft XR Fragments July 2024
+================================+=========================+
| metadata key | example value |
+================================+=========================+
@ -1821,13 +1787,20 @@ Internet-Draft XR Fragments July 2024
| * = 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
support where the metadata is/goes.
| These attributes can be scanned and presented during an href or
| src eye/mouse-over.
23. Accessibility interface
21. Accessibility interface
The addressibility of XR Fragments allows for unique 3D-to-text
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.)
4. the textlog contains aria-descriptions, and its narration
(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
for browser-backbutton)
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
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
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
available)
van Kammen Expires 13 January 2025 [Page 34]
Internet-Draft XR Fragments July 2024
24. Security Considerations
22. Security Considerations
The only dynamic parts are W3C Media Fragments
(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
(javascript) which can change URN too.
25. FAQ
23. FAQ
*Q:* Why is everything HTTP GET-based, what about POST/PUT/DELETE
HATEOS
@ -1940,6 +1898,14 @@ Internet-Draft XR Fragments July 2024
scripting language.
XR Fragments supports filtering objects in a scene only, because in
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.
Doing advanced scripting & networkrequests under the hood are
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
hypermedia documents (this turned out as a hypermedia antipattern).
26. authors
24. authors
* Leon van Kammen (@lvk@mastodon.online)
* Jens Finkhäuser (@jens@social.finkhaeuser.de)
van Kammen Expires 13 January 2025 [Page 35]
Internet-Draft XR Fragments July 2024
27. IANA Considerations
25. IANA Considerations
This document has no IANA actions.
28. Acknowledgments
26. Acknowledgments
* NLNET (https://nlnet.nl)
* Future of Text (https://futureoftext.org)
@ -1981,7 +1939,7 @@ Internet-Draft XR Fragments July 2024
* Brandel Zackernuk
* Mark Anderson
29. Appendix: Definitions
27. Appendix: Definitions
+=================+=============================================+
| definition | explanation |
@ -1996,6 +1954,14 @@ Internet-Draft XR Fragments July 2024
| 3D object | an object inside a scene characterized by |
| | 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 |
| | someprotocol (http://me.com/foo.glb#foo or |
| | e76f8efec8efce98e6f see interpeer.io |
@ -2010,14 +1976,6 @@ Internet-Draft XR Fragments July 2024
| | Object(nodes), relevant to machines and a |
| | 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 |
| | #pos=0,0,0&t=1,100 e.g. |
+-----------------+---------------------------------------------+
@ -2052,6 +2010,14 @@ Internet-Draft XR Fragments July 2024
| | indirectly visible/editable in XR. |
+-----------------+---------------------------------------------+
| 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 |
| | framerate-dropping, hence not used a lot in |
| | games) |
@ -2066,14 +2032,6 @@ Internet-Draft XR Fragments July 2024
| extrospective | outward sensemaking ("I'm fairly sure John |
| | 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 |
+-----------------+---------------------------------------------+
| (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 38]
van Kammen Expires 13 January 2025 [Page 37]

View File

@ -1105,12 +1105,12 @@ Some pointers for good UX (but not necessary to be XR Fragment compatible):</t>
| | | author.com/article.txt |
| index.gltf | +------------------------+
| │ | | |
| ├── ◻ article_canvas | | Hello friends. |
| ├── ◻ article_canvas | | Hello #friends |
| │ └ src: ://author.com/article.txt | | |
| │ | | @book{greatgatsby |
| └── ◻ note_canvas | | ... |
| └ src:`data:welcome human\n@book{sunday...}` | | } |
| | +------------------------+
| │ | +------------------------+
| └── ◻ note_canvas |
| └ 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>
</section>
<section anchor="transclusion-broken-link-resolution"><name>Transclusion (broken link) resolution</name>
<t>In spirit of Ted Nelson's 'transclusion resolution', there's a soft-mechanism to harden links &amp; minimize broken links in various ways:</t>
<section anchor="reflection-mapping"><name>Reflection Mapping</name>
<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">
<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 (HTTP) errorcode tags in <tt>src</tt> or <tt>href</tt> properties</li>
<li>in case of <tt>src</tt>: nesting a copy of the embedded object in the placeholder object (<tt>embeddedObject</tt>) will not be replaced when the request fails</li>
</ol>
<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[
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.
<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 │
│ │ │
│ │ #: #-offlinetext │
│ │ │
│ ├── ◻ 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@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.
│ └── ◻ embeddedObject &lt;--------- the meshdata inside embeddedObject will (not)
│ └ src: <eref target="https://foo.io/bar.gltf">https://foo.io/bar.gltf</eref> │ be flushed when the request (does not) succeed.
│ └ 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: <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>
</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 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[
+─────────────────────────────────────────────+
<t>+─────────────────────────────────────────────+
│ │
│ foo.usdz │
│ │ │
│ │ │
│ foo.usdz │<br />
│ │ │<br />
│ │ │<br />
│ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&-stopbutton │ (stop and hide stop-button)
│ │ │
│ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button)
│ │ │<br />
│ └── ◻ plane │
│ ├ play: #t=l:0,10 │
│ ├ 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} │
│ │
│ │
+─────────────────────────────────────────────+
]]>
</artwork>
+─────────────────────────────────────────────+</t>
<t>```</t>
</section>
<section anchor="additional-scene-metadata"><name>Additional scene metadata</name>

View File

@ -17,7 +17,6 @@
</head>
<body>
<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"
device-orientation-permission-ui
light="defaultLightsEnabled: false">

Binary file not shown.

View File

@ -9,16 +9,16 @@
<body style="overflow:hidden; padding:10%">
<h1>&lt;model-viewer&gt; example</h1>
<br><br>
<div style="width:90%; height:70vh">
<model-viewer
src="./../assets/index.glb"
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%"
style="width:100%; height:100%; border-radius:5px; border:1px solid #CCC"
>
</model-viewer>
</div>
<model-viewer
src="./../assets/index.glb"
environment-image="https://cdn.glitch.global/8e507517-31ff-4aa5-80c1-10ea6de9483d/white_furnace.hdr"
ar alt="XR Fragments demo scene" touch-action="none" disable-pan disable-zoom
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>
<script type="importmap">
{
@ -36,8 +36,8 @@
import { USDZLoader } from 'three/addons/loaders/USDZLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
const mv = document.querySelector("model-viewer");
const $url = document.querySelector('#url')
const mv = window.mv = document.querySelector("model-viewer");
const $url = document.querySelector('#url')
const orbitDefault = '50deg 90deg 1.0m'
function getSymbol(name) {
@ -52,34 +52,6 @@
} 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) => {
//.name, `${n.userData['aria-description'] || n.name}` )
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-target',`${pos.x}m ${pos.y}m ${pos.z}m`)
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)
}
@ -106,21 +82,7 @@
createHotspot(pos, n)
}
})
}
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';
}
})
setupClicks()
}
const setupClicks = () => {
@ -132,8 +94,9 @@
mv.cameraOrbit = dataset.orbit;
mv.fieldOfView = '45deg';
console.dir(node)
if( node && node.userData.XRF && node.userData.XRF.href ){
node.userData.XRF.href.exec({type:'click'})
if( node && node.userData.href ){
// xrf.navigator.to( node.userData.href )
//node.userData.XRF.href.exec({type:'click'})
console.log("clicked!")
}
}
@ -142,24 +105,49 @@
});
}
const clear = (obj) => {
while(obj.children.length > 0){
clear(obj.children[0]);
obj.remove(obj.children[0]);
const setupDefaultProjection = () => {
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")
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){
//in case of map, bumpMap, normalMap, envMap ...
Object.keys(obj.material).forEach(prop => {
if(!obj.material[prop])
return;
if(obj.material[prop] !== null && typeof obj.material[prop].dispose === 'function')
obj.material[prop].dispose();
})
obj.material.dispose();
}
}
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 opts = {
xrf,
@ -171,10 +159,9 @@
const renderer = mv[getSymbol("renderer")].threeRenderer
const controls = mv[getSymbol("controls")]
const camera = mv[getSymbol("scene")].getCamera()
clear(scene);
const url = mv.src
opts = {
opts = window.opts = {
...opts,
scene,
renderer,
@ -185,34 +172,31 @@
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
if( camera.parent == null ) scene.add(camera) // xr fragments expects in-scene camera
let xrf = opts.xrf.init(opts)
window.xrf = xrf
//
xrf.sceneRoot.children.map( (c) => (camera.id != c.id) && (c.visible = false) )
xrf.addEventListener('href', (opts) => {
console.log("href!")
console.dir(opts)
})
//
let url = mv.src
setupDefaultProjection(xrf)
// $url.value = url
// mark current loaded scene for deletion by xrfragment library (except camera)
xrf.addEventListener('navigateLoaded', function(opts){
console.dir(xrf.scene)
setupCSS()
setupHotspots(scene)
// setupDefaultProjection()
console.log("ready")
})
// 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()
//setupHotspots(scene)
//setupClicks()
console.log("ready")
}
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(){
// speak arrow keys
window.addEventListener('keydown', (e) => {
console.log(e.key)
if( e.key != "Tab" && e.key != "Enter" ) return
let subScene = xrf.scene.getObjectByName( xrf.frag.pos.last )
if( !subScene ) subScene = xrf.scene

View File

@ -53,7 +53,6 @@ xrf.loadModel = function(model,url,noadd){
let {directory,file,fragment,fileExt} = URI;
model.file = URI.file
xrf.model = model
xrf.scene = model.scene
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
@ -70,7 +69,7 @@ xrf.loadModel = function(model,url,noadd){
const defaultFragment = xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
if( !noadd ) xrf.add( model.scene )
// only change url when loading *another* file
@ -124,6 +123,7 @@ xrf.reset = () => {
}
xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object)
}