envmapping + bugfixes
This commit is contained in:
parent
0f7f482a3d
commit
b6715c1725
16 changed files with 58502 additions and 510 deletions
63
dist/xrfragment.aframe.all.js
vendored
63
dist/xrfragment.aframe.all.js
vendored
|
|
@ -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: {
|
||||||
},
|
},
|
||||||
|
|
|
||||||
63
dist/xrfragment.aframe.js
vendored
63
dist/xrfragment.aframe.js
vendored
|
|
@ -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
57979
dist/xrfragment.module.js
vendored
File diff suppressed because it is too large
Load diff
1
dist/xrfragment.plugin.frontend.js
vendored
1
dist/xrfragment.plugin.frontend.js
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
35
dist/xrfragment.three.js
vendored
35
dist/xrfragment.three.js
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
35
dist/xrfragment.three.module.js
vendored
35
dist/xrfragment.three.module.js
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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’s ‘transclusion resolution’, there’s a soft-mechanism to harden links & 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’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.</p>
|
||||||
|
|
||||||
<ol>
|
<p>”“”
|
||||||
<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>
|
+——————————–+<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
|
||||||
|
+——————————–+</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 & 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 & 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:
|
||||||
|
|
||||||
|
</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 <--------- the meshdata inside embeddedObject will (not)
|
│ └── ◻ embeddedObject <——— the meshdata inside embeddedObject will (not)
|
||||||
│ └ src: https://foo.io/bar.gltf │ be flushed when the request (does not) succeed.
|
│ └ src: <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.<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:
|
||||||
|
|
||||||
</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&-topics math</code></li>
|
|
||||||
<li><code>href: schoolB.edu/projects.gltf#math&-courses math</code></li>
|
|
||||||
<li><code>href: university.edu/projects.gltf#math&-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&-stopbutton │ (stop and hide stop-button)
|
│ │ └ href: #player=stop&-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>”`</p>
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h1 id="additional-scene-metadata">Additional scene metadata</h1>
|
<h1 id="additional-scene-metadata">Additional scene metadata</h1>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
|
||||||
|
|
|
||||||
|
|
@ -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 & 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 <--------- 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&-topics math</tt></li>
|
|
||||||
<li><tt>href: schoolB.edu/projects.gltf#math&-courses math</tt></li>
|
|
||||||
<li><tt>href: university.edu/projects.gltf#math&-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&-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&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>
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
@ -9,16 +9,16 @@
|
||||||
<body style="overflow:hidden; padding:10%">
|
<body style="overflow:hidden; padding:10%">
|
||||||
|
|
||||||
<h1><model-viewer> example</h1>
|
<h1><model-viewer> 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) )
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue