xrfragment/doc/RFC_XR_Fragments.html

1706 lines
80 KiB
HTML
Raw Normal View History

2023-09-01 14:20:02 +02:00
<!DOCTYPE html>
<html>
<head>
<title>XR Fragments</title>
<meta name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl">
<meta charset="utf-8">
</head>
<body>
<!-- for annotated version see: https://raw.githubusercontent.com/ietf-tools/rfcxml-templates-and-schemas/main/draft-rfcxml-general-template-annotated-00.xml -->
<style type="text/css">
body{
font-family: monospace;
2023-09-04 21:20:59 +02:00
max-width: 1000px;
2023-09-01 14:20:02 +02:00
font-size: 15px;
2023-09-21 15:28:37 +02:00
padding: 0% 10%;
2023-09-01 14:20:02 +02:00
line-height: 30px;
color:#555;
2023-09-18 11:03:18 +02:00
background:#F7F7F7;
2023-09-01 14:20:02 +02:00
}
h1 { margin-top:40px; }
pre{ line-height:18px; }
2023-09-02 21:44:57 +02:00
a,a:visited,a:active{ color: #70f; }
code{
border: 1px solid #AAA;
border-radius: 3px;
padding: 0px 5px 2px 5px;
}
2023-09-04 21:20:59 +02:00
pre{
line-height: 18px;
overflow: auto;
padding: 12px;
}
pre + code {
background:#DDD;
}
2023-09-02 21:44:57 +02:00
pre>code{
border:none;
border-radius:0px;
padding:0;
}
blockquote{
padding-left: 30px;
margin: 0;
border-left: 5px solid #CCC;
}
2023-09-04 21:20:59 +02:00
th {
border-bottom: 1px solid #000;
text-align: left;
padding-right:45px;
padding-left:7px;
background: #DDD;
}
td {
border-bottom: 1px solid #CCC;
font-size:13px;
}
2023-09-01 14:20:02 +02:00
</style>
<br>
<h1>XR Fragments</h1>
<br>
<pre>
stream: IETF
area: Internet
status: informational
author: Leon van Kammen
date: 2023-04-12T00:00:00Z
workgroup: Internet Engineering Task Force
value: draft-XRFRAGMENTS-leonvankammen-00
</pre>
<h1 class="special" id="abstract">Abstract</h1>
2024-04-25 21:06:03 +02:00
<p>This draft is a specification for 4D URI&rsquo;s &amp; <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> navigation, to enable a spatial web for hypermedia browsers with- or without a network-connection.<br>
2024-02-08 14:05:44 +01:00
The specification uses <a href="https://www.w3.org/TR/media-frags/">W3C Media Fragments</a> and <a href="https://www.rfc-editor.org/rfc/rfc6570">URI Templates (RFC6570)</a> to promote spatial addressibility, sharing, navigation, filtering and databinding objects for (XR) Browsers.<br>
2024-04-25 21:06:03 +02:00
XR Fragments allows us to better use existing metadata inside 3D scene(files), by connecting it to proven technologies like <a href="https://en.wikipedia.org/wiki/URI_fragment">URI Fragments</a>.<br>
XR Fragments views spatial webs thru the lens of 3D scene URI&rsquo;s, rather than thru code(frameworks) or protocol-specific browsers (webbrowser e.g.).</p>
2023-09-05 19:14:10 +02:00
<blockquote>
<p>Almost every idea in this document is demonstrated at <a href="https://xrfragment.org">https://xrfragment.org</a></p>
</blockquote>
2023-09-01 14:20:02 +02:00
<section data-matter="main">
<h1 id="introduction">Introduction</h1>
2024-04-25 21:06:03 +02:00
<p>How can we add more control to existing text and 3D scenes, without introducing new dataformats?<br>
Historically, there&rsquo;s many attempts to create the ultimate 3D fileformat.<br>
2023-12-06 16:40:55 +01:00
The lowest common denominator is: designers describing/tagging/naming things using <strong>plain text</strong>.<br>
XR Fragments exploits the fact that all 3D models already contain such metadata:</p>
2024-01-30 10:58:00 +01:00
<p><strong>XR Fragments allows controlling of metadata in 3D scene(files) using URI&rsquo;s</strong></p>
2023-12-06 16:40:55 +01:00
2024-01-30 10:58:00 +01:00
<p>It solves:</p>
2023-09-01 14:20:02 +02:00
2023-09-04 21:20:59 +02:00
<ol>
2024-02-23 11:51:05 +01:00
<li>addressibility and <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> navigation of 3D scenes/objects: <a href="https://en.wikipedia.org/wiki/URI_fragment">URI Fragments</a> using src/href spatial metadata</li>
2024-01-30 10:58:00 +01:00
<li>Interlinking text &amp; spatial objects by collapsing space into a Word Graph (XRWG) to show <a href="#visible-links">visible links</a></li>
2023-09-21 13:05:30 +02:00
<li>unlocking spatial potential of the (originally 2D) hashtag (which jumps to a chapter) for navigating XR documents</li>
2024-02-23 11:51:05 +01:00
<li>refraining from introducing scripting-engines for mundane tasks (and preventing its inevitable security-headaches)</li>
2024-02-29 15:07:20 +01:00
<li>the gap between text an 3d objects: object-names directly map to hashtags (=fragments), which allows 3D to text transcription.</li>
2023-09-04 21:20:59 +02:00
</ol>
<blockquote>
<p>NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible</p>
</blockquote>
2023-09-01 14:20:02 +02:00
2023-09-05 19:14:10 +02:00
<h1 id="core-principle">Core principle</h1>
2024-01-30 10:58:00 +01:00
<p><strong>XR Fragments allows controlling 3D models using URLs, based on (non)existing metadata via URI&rsquo;s</strong></p>
2023-12-06 16:40:55 +01:00
<p>XR Fragments tries to seek to connect the world of text (semantical web / RDF), and the world of pixels.<br>
2024-01-30 10:58:00 +01:00
Instead of forcing authors to combine 3D/2D objects programmatically (publishing thru a game-editor e.g.), XR Fragments <strong>integrates all</strong> which allows a universal viewing experience.<br></p>
<pre><code> +───────────────────────────────────────────────────────────────────────────────────────────────+
│ │
│ U R N │
│ U R L | │
│ | |-----------------+--------| │
│ +--------------------------------------------------| │
│ | │
│ + https://foo.com/some/foo/scene.glb#someview &lt;-- http URI (=URL and has URN) │
│ | │
│ + ipfs://cfe0987ec9r9098ecr/cats.fbx#someview &lt;-- an IPFS URI (=URL and has URN) │
│ │
│ ec09f7e9cf8e7f09c8e7f98e79c09ef89e000efece8f7ecfe9fe &lt;-- an interpeer URI │
│ │
│ │
│ |------------------------+-------------------------| │
│ | │
│ U R I │
│ │
+───────────────────────────────────────────────────────────────────────────────────────────────+
</code></pre>
<p>Fact: our typical browser URL&rsquo;s are just <strong>a possible implementation</strong> of URI&rsquo;s (for untapped humancentric potential of URI&rsquo;s <a href="https://interpeer.io">see interpeer.io</a>)</p>
<blockquote>
<p>XR Fragments does not look at XR (or the web) thru the lens of HTML or URLs.<br>But approaches things from a higherlevel feedbackloop/hypermedia browser-perspective.</p>
</blockquote>
<p>Below you can see how this translates back into good-old URLs:</p>
<pre><code> +───────────────────────────────────────────────────────────────────────────────────────────────+
│ │
│ the soul of any URL: ://macro /meso ?micro #nano │
│ │
│ 2D URL: ://library.com /document ?search #chapter │
2024-02-26 12:34:21 +01:00
│ xrf:// │
2024-01-30 10:58:00 +01:00
│ 4D URL: ://park.com /4Dscene.fbx ─&gt; ?other.glb ─&gt; #view ───&gt; hashbus │
│ │ #filter │ │
│ │ #tag │ │
│ │ (hypermediatic) #material │ │
│ │ ( feedback ) #animation │ │
│ │ ( loop ) #texture │ │
│ │ #variable │ │
│ │ │ │
│ XRWG &lt;─────────────────────&lt;─────────────+ │
│ │ │ │
│ └─ objects ──────────────&gt;─────────────+ │
│ │
│ │
+───────────────────────────────────────────────────────────────────────────────────────────────+
</code></pre>
<blockquote>
2024-02-26 12:34:21 +01:00
<p>?-linked and #-linked navigation are JUST one possible way to implement XR Fragments: the essential goal is to allow a Hypermediatic FeedbackLoop (HFL) between external and internal 4D navigation.</p>
2024-01-30 10:58:00 +01:00
</blockquote>
2024-02-29 15:07:20 +01:00
<p>Traditional webbrowsers can become 4D document-ready by:</p>
2024-02-26 12:34:21 +01:00
<h1 id="hypermediatic-feedbackloop-for-xr-browsers">Hypermediatic FeedbackLoop for XR browsers</h1>
2024-04-25 21:06:03 +02:00
<p><code>href</code> metadata traditionally implies <strong>click</strong> AND <strong>navigate</strong>, however XR Fragments adds stateless <strong>click</strong> (<code>xrf://#....</code>) or <strong>navigate</strong> (<code>xrf://#pos=...</code>)
2024-02-26 12:34:21 +01:00
as well (which allows many extra interactions which otherwise need a scripting language). This is known as <strong>hashbus</strong>-only events (see image above).</p>
<blockquote>
<p>Being able to use the same URI Fragment DSL for navigation (<code>href: #foo</code>) as well as interactions (<code>href: xrf://#bar</code>) greatly simplifies implementation, increases HFL, and reduces need for scripting languages.</p>
</blockquote>
<p>This opens up the following benefits for traditional &amp; future webbrowsers:</p>
2024-01-30 10:58:00 +01:00
<ul>
2024-02-26 12:34:21 +01:00
<li><a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> loading/clicking 3D assets (gltf/fbx e.g.) natively (with or without using HTML).</li>
<li>allowing 3D assets/nodes to publish XR Fragments to themselves/eachother using the <code>xrf://</code> hashbus</li>
2024-01-30 10:58:00 +01:00
<li>collapsing the 3D scene to an wordgraph (for essential navigation purposes) controllable thru a hash(tag)bus</li>
2024-02-26 12:34:21 +01:00
<li>completely bypassing the security-trap of loading external scripts (by loading 3D model-files, not HTML-javascriptable resources)</li>
2024-01-30 10:58:00 +01:00
</ul>
<p>XR Fragments itself are <a href="https://github.com/coderofsalvation/hypermediatic">hypermediatic</a> and HTML-agnostic, though pseudo-XR Fragment browsers <strong>can</strong> be implemented on top of HTML/Javascript.</p>
2023-09-05 19:14:10 +02:00
2023-09-18 11:03:18 +02:00
<table>
<thead>
<tr>
<th>principle</th>
<th>XR 4D URL</th>
<th>HTML 2D URL</th>
</tr>
</thead>
<tbody>
<tr>
<td>the XRWG</td>
<td>wordgraph (collapses 3D scene to tags)</td>
<td>Ctrl-F (find)</td>
</tr>
<tr>
<td>the hashbus</td>
2023-12-06 16:40:55 +01:00
<td>hashtags alter camera/scene/object-projections</td>
<td>hashtags alter document positions</td>
2023-09-18 11:03:18 +02:00
</tr>
2023-09-21 13:05:30 +02:00
<tr>
<td>src metadata</td>
<td>renders content and offers sourceportation</td>
<td>renders content</td>
</tr>
<tr>
<td>href metadata</td>
2023-09-21 16:20:38 +02:00
<td>teleports to other XR document</td>
<td>jumps to other HTML document</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
<td>href metadata</td>
2023-12-06 16:40:55 +01:00
<td>triggers predefined view</td>
<td>Media fragments</td>
</tr>
<tr>
<td>href metadata</td>
<td>triggers camera/scene/object/projections</td>
<td>n/a</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
<td>href metadata</td>
2023-09-21 16:20:38 +02:00
<td>draws visible connection(s) for XRWG &lsquo;tag&rsquo;</td>
2023-12-06 16:40:55 +01:00
<td>n/a</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
<td>href metadata</td>
2023-12-06 16:40:55 +01:00
<td>filters certain (in)visible objects</td>
<td>n/a</td>
2023-09-21 13:05:30 +02:00
</tr>
2024-02-26 12:34:21 +01:00
<tr>
<td>href metadata</td>
<td>href=&ldquo;xrf://#-foo&amp;bar&rdquo;</td>
<td>href=&ldquo;javascript:hideFooAndShowBar()`</td>
</tr>
<tr>
<td></td>
<td>(this does not update topLevel URI)</td>
<td>(this is non-standard, non-hypermediatic)</td>
</tr>
2023-09-18 11:03:18 +02:00
</tbody>
</table>
2023-09-05 19:14:10 +02:00
2024-02-26 12:34:21 +01:00
<blockquote>
<p>An important aspect of HFL is that URI Fragments can be triggered without updating the top-level URI (default href-behaviour) thru their own &lsquo;bus&rsquo; (<code>xrf://#.....</code>). This decoupling between navigation and interaction prevents non-standard things like (<code>href</code>:<code>javascript:dosomething()</code>).</p>
</blockquote>
2023-09-07 15:53:32 +02:00
<h1 id="conventions-and-definitions">Conventions and Definitions</h1>
2023-09-06 15:43:29 +02:00
2023-09-07 15:53:32 +02:00
<p>See appendix below in case certain terms are not clear.</p>
2023-09-04 21:20:59 +02:00
2024-01-30 10:58:00 +01:00
<h2 id="xr-fragment-url-grammar">XR Fragment URL Grammar</h2>
<p>For typical HTTP-like browsers/applications:</p>
2023-09-18 11:03:18 +02:00
<pre><code>reserved = gen-delims / sub-delims
gen-delims = &quot;#&quot; / &quot;&amp;&quot;
sub-delims = &quot;,&quot; / &quot;=&quot;
</code></pre>
<blockquote>
<p>Example: <code>://foo.com/my3d.gltf#pos=1,0,0&amp;prio=-5&amp;t=0,100</code></p>
</blockquote>
<table>
<thead>
<tr>
<th>Demo</th>
<th>Explanation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pos=1,2,3</code></td>
<td>vector/coordinate argument e.g.</td>
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>pos=1,2,3&amp;rot=0,90,0&amp;foo</code></td>
2023-09-18 11:03:18 +02:00
<td>combinators</td>
</tr>
</tbody>
</table>
<blockquote>
<p>this is already implemented in all browsers</p>
</blockquote>
2024-01-30 10:58:00 +01:00
<p>Pseudo (non-native) browser-implementations (supporting XR Fragments using HTML+JS e.g.) can use the <code>?</code> search-operator to address outbound content.<br>
In other words, the URL updates to: <code>https://me.com?https://me.com/other.glb</code> when navigating to <code>https://me.com/other.glb</code> from inside a <code>https://me.com</code> WebXR experience e.g.<br>
That way, if the link gets shared, the XR Fragments implementation at <code>https://me.com</code> can load the latter (and still indicates which XR Fragments entrypoint-experience/client was used).</p>
2024-04-25 21:06:03 +02:00
<h1 id="spatial-referencing-3d">Spatial Referencing 3D</h1>
<p>XR Fragments assume the following objectname-to-URIFragment mapping:</p>
<pre><code>
my.io/scene.fbx
+─────────────────────────────+
│ sky │ src: http://my.io/scene.fbx#sky (includes building,mainobject,floor)
│ +─────────────────────────+ │
│ │ building │ │ src: http://my.io/scene.fbx#building (includes mainobject,floor)
│ │ +─────────────────────+ │ │
│ │ │ mainobject │ │ │ src: http://my.io/scene.fbx#mainobject (includes floor)
│ │ │ +─────────────────+ │ │ │
│ │ │ │ floor │ │ │ │ src: http://my.io/scene.fbx#floor (just floor object)
│ │ │ │ │ │ │ │
│ │ │ +─────────────────+ │ │ │
│ │ +─────────────────────+ │ │
│ +─────────────────────────+ │
+─────────────────────────────+
</code></pre>
<blockquote>
<p>Every 3D fileformat supports named 3D object, and this name allows URLs (fragments) to reference them (and their children objects).</p>
</blockquote>
<p>Clever nested design of 3D scenes allow great ways for re-using content, and/or previewing scenes.<br>
For example, to render a portal with a preview-version of the scene, create an 3D object with:</p>
<ul>
<li>href: <code>https://scene.fbx</code></li>
<li>src: <code>https://otherworld.gltf#mainobject</code></li>
</ul>
<blockquote>
<p>It also allows <strong>sourceportation</strong>, which basically means the enduser can teleport to the original XR Document of an <code>src</code> embedded object, and see a visible connection to the particular embedded object. Basically an embedded link becoming an outbound link by activating it.</p>
</blockquote>
2023-09-04 21:20:59 +02:00
<h1 id="list-of-uri-fragments">List of URI Fragments</h1>
<table>
<thead>
<tr>
<th>fragment</th>
<th>type</th>
<th>example</th>
<th>info</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>#pos</code></td>
<td>vector3</td>
<td><code>#pos=0.5,0,0</code></td>
2023-09-18 11:03:18 +02:00
<td>positions camera (or XR floor) to xyz-coord 0.5,0,0,</td>
2023-09-04 21:20:59 +02:00
</tr>
<tr>
<td><code>#rot</code></td>
<td>vector3</td>
<td><code>#rot=0,90,0</code></td>
<td>rotates camera to xyz-coord 0.5,0,0</td>
</tr>
<tr>
2024-02-26 12:34:21 +01:00
<td><a href="https://www.w3.org/TR/media-frags/">Media Fragments</a></td>
2024-02-01 13:28:17 +01:00
<td><a href="#media%20fragments%20and%20datatypes">media fragment</a></td>
2024-02-16 17:34:32 +01:00
<td><code>#t=0,2&amp;loop</code></td>
<td>play (and loop) 3D animation from 0 seconds till 2 seconds</td>
2023-09-04 21:20:59 +02:00
</tr>
2024-02-02 13:49:37 +01:00
<tr>
<td></td>
<td></td>
<td></td>
<td>but can also crop, animate &amp; configure uv-coordinates/shader uniforms</td>
</tr>
2023-09-04 21:20:59 +02:00
</tbody>
</table>
2023-12-06 16:40:55 +01:00
<h2 id="list-of-metadata-for-3d-nodes">List of metadata for 3D nodes</h2>
2023-09-04 21:20:59 +02:00
<table>
<thead>
<tr>
<th>key</th>
<th>type</th>
<th>example (JSON)</th>
2023-09-21 13:05:30 +02:00
<th>function</th>
<th>existing compatibility</th>
2023-09-04 21:20:59 +02:00
</tr>
</thead>
<tbody>
<tr>
<td><code>href</code></td>
<td>string</td>
<td><code>&quot;href&quot;: &quot;b.gltf&quot;</code></td>
2023-09-21 13:05:30 +02:00
<td>XR teleport</td>
<td>custom property in 3D fileformats</td>
2023-09-04 21:20:59 +02:00
</tr>
<tr>
<td><code>src</code></td>
<td>string</td>
2023-09-18 11:03:18 +02:00
<td><code>&quot;src&quot;: &quot;#cube&quot;</code></td>
2023-09-21 13:05:30 +02:00
<td>XR embed / teleport</td>
<td>custom property in 3D fileformats</td>
2023-09-04 21:20:59 +02:00
</tr>
2023-10-12 17:04:46 +02:00
<tr>
<td><code>tag</code></td>
<td>string</td>
<td><code>&quot;tag&quot;: &quot;cubes geo&quot;</code></td>
2023-12-06 16:40:55 +01:00
<td>tag object (for filter-use / XRWG highlighting)</td>
2023-10-12 17:04:46 +02:00
<td>custom property in 3D fileformats</td>
</tr>
2024-02-23 11:51:05 +01:00
<tr>
<td><code>#</code></td>
<td>string</td>
<td><code>&quot;#&quot;: &quot;#mypreset</code></td>
<td>trigger default fragment on load</td>
<td>custom property in 3D fileformats</td>
</tr>
2023-09-04 21:20:59 +02:00
</tbody>
</table>
2023-12-06 16:40:55 +01:00
<blockquote>
2023-09-21 13:05:30 +02:00
<p>Supported popular compatible 3D fileformats: <code>.gltf</code>, <code>.obj</code>, <code>.fbx</code>, <code>.usdz</code>, <code>.json</code> (THREE.js), <code>.dae</code> and so on.</p>
2023-12-06 16:40:55 +01:00
</blockquote>
2024-02-08 14:05:44 +01:00
<h2 id="fragment-to-metadata-mapping">Fragment-to-metadata mapping</h2>
2023-12-06 16:40:55 +01:00
2024-02-08 14:05:44 +01:00
<p>These are automatic fragment-to-metadata mappings, which only trigger if the 3D scene metadata matches a specific identifier:</p>
2023-12-06 16:40:55 +01:00
<table>
2024-02-01 13:28:17 +01:00
<thead>
<tr>
2024-02-08 14:05:44 +01:00
<th></th>
2024-02-01 13:28:17 +01:00
<th>fragment</th>
<th>type</th>
<th>example</th>
<th>info</th>
</tr>
</thead>
2023-12-06 16:40:55 +01:00
<tbody>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>PRESET</strong></td>
<td><code>#&lt;preset&gt;</code></td>
2024-02-01 13:28:17 +01:00
<td>string</td>
<td><code>#cubes</code></td>
2024-02-08 14:05:44 +01:00
<td>evaluates preset (<code>#foo&amp;bar</code>) defined in 3D Object metadata (<code>#cubes: #foo&amp;bar</code> e.g.) while URL-browserbar reflects <code>#cubes</code>. Only works when metadata-key starts with <code>#</code></td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>FOCUS</strong></td>
2024-02-01 13:28:17 +01:00
<td><code>#&lt;tag_or_objectname&gt;</code></td>
<td>string</td>
<td><code>#person</code></td>
2024-02-08 14:05:44 +01:00
<td>(and show) object(s) with <code>tag: person</code> or name <code>person</code> (XRWG lookup)</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>FILTERS</strong></td>
<td><code>#[!][-]&lt;tag_or_objectname&gt;[*]</code></td>
2024-02-01 13:28:17 +01:00
<td>string</td>
<td><code>#person</code> (<code>#-person</code>)</td>
2024-02-08 14:05:44 +01:00
<td>will reset (<code>!</code>), show/focus or hide (<code>-</code>) focus object(s) with <code>tag: person</code> or name <code>person</code> by looking up XRWG (<code>*</code>=including children)</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>CAMERASWITCH</strong></td>
2024-02-01 13:28:17 +01:00
<td><code>#&lt;cameraname&gt;</code></td>
<td>string</td>
<td><code>#cam01</code></td>
2024-02-08 14:05:44 +01:00
<td>sets camera with name <code>cam01</code> as active camera</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>MATERIALUPDATE</strong></td>
<td><code>#&lt;tag_or_objectname&gt;[*]=&lt;materialname&gt;</code></td>
2024-02-01 13:28:17 +01:00
<td>string=string</td>
<td><code>#car=metallic</code></td>
2024-02-08 14:05:44 +01:00
<td>sets material of car to material with name <code>metallic</code> (<code>*</code>=including children)</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
<td></td>
<td></td>
2024-02-02 13:49:37 +01:00
<td></td>
2024-02-08 14:05:44 +01:00
<td><code>#soldout*=halfopacity</code></td>
2024-02-01 13:28:17 +01:00
<td>set material of objects tagged with <code>product</code> to material with name <code>metallic</code></td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-08 14:05:44 +01:00
<td><strong>VARIABLE UPDATE</strong></td>
<td><code>#&lt;variable&gt;=&lt;metadata-key&gt;</code></td>
<td>string=string</td>
<td><code>#foo=bar</code></td>
<td>sets <a href="https://www.rfc-editor.org/rfc/rfc6570">URI Template</a> variable <code>foo</code> to the value <code>#t=0</code> from <strong>existing</strong> object metadata (<code>bar</code>:<code>#t=0</code> e.g.), This allows for reactive <a href="https://www.rfc-editor.org/rfc/rfc6570">URI Template</a> defined in object metadata elsewhere (<code>src</code>:<code>://m.com/cat.mp4#{foo}</code> e.g., to play media using <a href="https://www.w3.org/TR/media-frags/#valid-uri">media fragment URI</a>). NOTE: metadata-key should not start with <code>#</code></td>
2024-02-02 13:49:37 +01:00
</tr>
2024-02-16 17:34:32 +01:00
<tr>
<td><strong>ANIMATION</strong></td>
<td><code>#&lt;tag_or_objectname&gt;=&lt;animationname&gt;</code></td>
<td>string=string</td>
<td><code>#people=walk</code> <code>#people=noanim</code></td>
<td>assign a different animation to object(s)</td>
</tr>
2024-02-01 13:28:17 +01:00
</tbody>
</table>
2023-12-06 16:40:55 +01:00
2024-02-01 13:28:17 +01:00
<h2 id="media-fragments-and-datatypes">media fragments and datatypes</h2>
2024-02-02 13:49:37 +01:00
<blockquote>
<p>NOTE: below the word &lsquo;play&rsquo; applies to 3D animations embedded in the 3D scene(file) <strong>but also</strong> media defined in <code>src</code>-metadata like audio/video-files (mp3/mp4 e.g.)</p>
</blockquote>
2024-02-01 13:28:17 +01:00
<table>
<thead>
2023-12-06 16:40:55 +01:00
<tr>
2024-02-01 13:28:17 +01:00
<th>type</th>
<th>syntax</th>
<th>example</th>
<th>info</th>
2023-12-06 16:40:55 +01:00
</tr>
2024-02-01 13:28:17 +01:00
</thead>
2023-12-06 16:40:55 +01:00
2024-02-01 13:28:17 +01:00
<tbody>
2023-12-06 16:40:55 +01:00
<tr>
2024-02-01 13:28:17 +01:00
<td>vector2</td>
<td>x,y</td>
<td>2,3.0</td>
<td>2-dimensional vector</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
2024-02-01 13:28:17 +01:00
<td>vector3</td>
<td>x,y,z</td>
<td>2,3.0,4</td>
<td>3-dimensional vector</td>
</tr>
<tr>
2024-02-02 13:49:37 +01:00
<td>temporal W3C media fragment</td>
<td>t=x</td>
2024-02-01 13:28:17 +01:00
<td>0</td>
2024-02-05 11:17:16 +01:00
<td>play from 0 seconds to end (and stop)</td>
2024-02-01 13:28:17 +01:00
</tr>
<tr>
2024-02-02 13:49:37 +01:00
<td>temporal W3C media fragment</td>
<td>t=x,y</td>
2024-02-01 13:28:17 +01:00
<td>0,2</td>
2024-02-05 11:17:16 +01:00
<td>play from 0 seconds till 2 seconds (and stop)</td>
2024-02-01 13:28:17 +01:00
</tr>
2024-02-08 14:05:44 +01:00
<tr>
<td>temporal W3C media fragment *</td>
<td>s=x</td>
2024-02-05 11:17:16 +01:00
<td>1</td>
<td>set playback speed of audio/video/3D anim</td>
</tr>
<tr>
2024-02-05 11:17:16 +01:00
<td>temporal W3C media fragment *</td>
<td>[-]loop</td>
<td>loop</td>
<td>enable looped playback of audio/video/3D anim</td>
</tr>
<tr>
<td></td>
<td></td>
<td>-loop</td>
<td>disable looped playback (does not affect playbackstate of media)</td>
2023-12-06 16:40:55 +01:00
</tr>
2023-09-01 14:20:02 +02:00
2023-12-06 16:40:55 +01:00
<tr>
<td>vector2</td>
2024-02-23 11:51:05 +01:00
<td>uv=u,v,uspeed,vspeed</td>
<td>0,0</td>
<td>set uv offset instantly (default speed = <code>1,1</code>)</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
<td></td>
<td></td>
<td>+0.5,+0.5</td>
<td>scroll instantly by adding 0.5 to the current uv coordinates</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
<td></td>
<td></td>
<td>0.2,1,0.1,0.1</td>
<td>scroll (lerp) to uv coordinate <code>0,2,1</code> with <code>0.1</code> units per second</td>
2023-12-06 16:40:55 +01:00
</tr>
<tr>
<td></td>
<td></td>
<td>0,0,0,+0.1</td>
<td>scroll v coordinates with <code>0.1</code> units per second (infinitely)</td>
</tr>
<tr>
2024-02-16 17:34:32 +01:00
<td></td>
<td></td>
<td>+0.5,+0.5</td>
<td>scroll instantly by adding 0.5 to the current uv coordinates</td>
</tr>
2024-04-25 21:06:03 +02:00
<tr>
<td>media parameter (shader uniform)</td>
<td>u:<uniform>=<string</td>
<td>float</td>
<td>vec2</td>
</tr>
2023-12-06 16:40:55 +01:00
</tbody>
</table>
2024-02-08 14:05:44 +01:00
<blockquote>
<p>* = this is extending the <a href="https://www.w3.org/TR/media-frags/#mf-advanced">W3C media fragments</a> with (missing) playback/viewport-control. Normally <code>#t=0,2</code> implies setting start/stop-values AND starting playback, whereas <code>#s=0&amp;loop</code> allows pausing a video, speeding up/slowing down media, as well as enabling/disabling looping.</p>
2024-02-23 11:51:05 +01:00
<p>The rationale for <code>uv</code> is that the <code>xywh</code> Media Fragment deals with rectangular media, which does not translate well to 3D models (which use triangular polygons, not rectangular) positioned by uv-coordinates. This also explains the absense of a <code>scale</code> or <code>rotate</code> primitive, which is challenged by this, as well as multiple origins (mesh- or texture).</p>
2024-02-08 14:05:44 +01:00
</blockquote>
2024-02-05 11:17:16 +01:00
<p>Example URI&rsquo;s:</p>
<ul>
<li><code>https://images.org/credits.jpg#uv=0,0,0,+0.1</code> (infinite vertical texturescrolling)</li>
2024-02-26 12:34:21 +01:00
<li><code>https://video.org/organogram.mp4#t=0&amp;loop&amp;uv=0.1,0.1,0.3,0.3</code> (animated tween towards region in looped video)</li>
2024-02-23 11:51:05 +01:00
<li><code>https://shaders.org/plasma.glsl#t=0&amp;u:col2=0,1,0</code> (red-green shader plasma starts playing from time-offset 0)</li>
2024-02-05 11:17:16 +01:00
</ul>
2023-12-06 16:40:55 +01:00
2024-02-02 13:49:37 +01:00
<pre><code> +──────────────────────────────────────────────────────────+
│ │
│ index.gltf#playall │
│ │ │
2024-02-08 14:05:44 +01:00
│ ├ # : #t=0&amp;shared=play │ apply default XR Fragment on load (`t` plays global 3D animation timeline)
2024-06-17 07:59:51 +02:00
│ ├ play : #t=0&amp;loop │ variable for [URI Templates (RFC6570)](https://www.rfc-editor.org/rfc/rfc6570)
2024-02-08 14:05:44 +01:00
│ │ │
│ ├── ◻ plane (with material) │
2024-02-23 11:51:05 +01:00
│ │ └ #: #uv=0,0,0,+0.1 │ infinite texturescroll `v` of uv·coordinates with 0.1/fps
2024-02-08 14:05:44 +01:00
│ │ │
│ ├── ◻ plane │
2024-02-23 11:51:05 +01:00
│ │ └ src: foo.jpg#uv=0,0,0,+0.1 │ infinite texturescroll `v` of uv·coordinates with 0.1/fps
2024-02-02 13:49:37 +01:00
│ │ │
2024-02-08 14:05:44 +01:00
│ ├── ◻ media │
│ │ └ src: cat.mp4#t=l:2,10&amp;uv=0.5,0.5 │ loop cat.mp4 (or mp3/wav/jpg) between 2 and 10 seconds (uv's shifted with 0.5,0.5)
│ │ │
│ └── ◻ wall │
│ ├ href: #color=blue │ updates uniform values (IFS shader e.g.)
│ ├ blue: t=0&amp;u:col=0,0,1 │ variable for [Level1 URI Templates (RFC6570)](https://www.rfc-editor.org/rfc/rfc6570)
│ └ src: ://a.com/art.glsl#{color}&amp;{shared} │ .fs/.vs/.glsl/.wgsl etc shader [Level1 URI Template (RFC6570)](https://www.rfc-editor.org/rfc/rfc6570)
2024-02-02 13:49:37 +01:00
│ │
│ │
+──────────────────────────────────────────────────────────+
2024-02-05 11:17:16 +01:00
2024-02-08 14:05:44 +01:00
&gt; NOTE: URI Template variables are immutable and respect scope: in other words, the end-user cannot modify `blue` by entering an URL like `#blue=.....` in the browser URL, and `blue` is not accessible by the plane/media-object (however `{play}` would work).
2024-02-05 11:17:16 +01:00
2024-02-02 13:49:37 +01:00
</code></pre>
2023-09-21 13:05:30 +02:00
<h1 id="navigating-3d">Navigating 3D</h1>
2023-09-07 14:06:50 +02:00
2023-09-18 11:03:18 +02:00
<table>
<thead>
<tr>
2023-09-21 13:05:30 +02:00
<th>fragment</th>
<th>type</th>
<th>functionality</th>
2023-09-18 11:03:18 +02:00
</tr>
</thead>
2023-09-07 14:06:50 +02:00
2023-09-18 11:03:18 +02:00
<tbody>
<tr>
2023-09-21 13:05:30 +02:00
<td><b>#pos</b>=0,0,0</td>
2023-12-06 16:40:55 +01:00
<td>vector3 or string</td>
<td>(re)position camera based on coordinates directly, or indirectly using objectname (its worldposition)</td>
2023-09-18 11:03:18 +02:00
</tr>
<tr>
2023-09-21 13:05:30 +02:00
<td><b>#rot</b>=0,90,0</td>
<td>vector3</td>
<td>rotate camera</td>
2023-09-18 11:03:18 +02:00
</tr>
</tbody>
</table>
2023-09-21 13:05:30 +02:00
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js">» example implementation</a><br>
<a href="https://github.com/coderofsalvation/xrfragment/issues/5">» discussion</a><br></p>
2023-09-07 14:06:50 +02:00
<ol>
2023-09-21 13:05:30 +02:00
<li>the Y-coordinate of <code>pos</code> identifies the floorposition. This means that desktop-projections usually need to add 1.5m (average person height) on top (which is done automatically by VR/AR headsets).</li>
<li>set the position of the camera accordingly to the vector3 values of <code>#pos</code></li>
<li><code>rot</code> sets the rotation of the camera (only for non-VR/AR headsets)</li>
2024-04-25 21:06:03 +02:00
<li>mediafragment <code>t</code> in the top-URL sets the playbackspeed and animation-range of the global scene animation</li>
2024-05-10 13:41:20 +02:00
<li>before scene load: the scene is cleared</li>
<li>after scene load: in case the scene (rootnode) contains an <code>#</code> default view with a fragment value: execute non-positional fragments via the hashbus (no top-level URL change)</li>
<li>after scene load: in case the scene (rootnode) contains an <code>#</code> default view with a fragment value: execute positional fragment via the hashbus + update top-level URL</li>
<li>in case of no default <code>#</code> view on the scene (rootnode), default player(rig) position <code>0,0,0</code> is assumed.</li>
<li>in case a <code>href</code> does not mention any <code>pos</code>-coordinate, the current position will be assumed</li>
2023-09-07 14:06:50 +02:00
</ol>
2023-09-21 13:05:30 +02:00
<p>Here&rsquo;s an ascii representation of a 3D scene-graph which contains 3D objects <code></code> and their metadata:</p>
2023-09-07 14:06:50 +02:00
2023-09-21 13:05:30 +02:00
<pre><code> +────────────────────────────────────────────────────────+
│ │
│ index.gltf │
│ │ │
│ ├── ◻ buttonA │
│ │ └ href: #pos=1,0,1&amp;t=100,200 │
│ │ │
│ └── ◻ buttonB │
│ └ href: other.fbx │ &lt;── file─agnostic (can be .gltf .obj etc)
│ │
+────────────────────────────────────────────────────────+
2023-09-07 14:06:50 +02:00
2023-09-21 13:05:30 +02:00
</code></pre>
2023-09-07 14:06:50 +02:00
2023-09-21 13:05:30 +02:00
<p>An XR Fragment-compatible browser viewing this scene, allows the end-user to interact with the <code>buttonA</code> and <code>buttonB</code>.<br>
In case of <code>buttonA</code> the end-user will be teleported to another location and time in the <strong>current loaded scene</strong>, but <code>buttonB</code> will <strong>replace the current scene</strong> with a new one, like <code>other.fbx</code>, and assume <code>pos=0,0,0</code>.</p>
2023-09-07 14:06:50 +02:00
2023-09-21 13:05:30 +02:00
<h1 id="top-level-url-processing">Top-level URL processing</h1>
2023-09-07 14:06:50 +02:00
<blockquote>
2023-09-21 13:05:30 +02:00
<p>Example URL: <code>://foo/world.gltf#cube&amp;pos=0,0,0</code></p>
2023-09-07 14:06:50 +02:00
</blockquote>
2023-09-21 13:05:30 +02:00
<p>The URL-processing-flow for hypermedia browsers goes like this:</p>
2023-09-07 14:06:50 +02:00
2023-09-21 13:30:14 +02:00
<ol>
<li>IF a <code>#cube</code> matches a custom property-key (of an object) in the 3D file/scene (<code>#cube</code>: <code>#......</code>) <b>THEN</b> execute that predefined_view.</li>
<li>IF scene operators (<code>pos</code>) and/or animation operator (<code>t</code>) are present in the URL then (re)position the camera and/or animation-range accordingly.</li>
<li>IF no camera-position has been set in <b>step 1 or 2</b> update the top-level URL with <code>#pos=0,0,0</code> (<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/navigator.js#L31]]">example</a>)</li>
<li>IF a <code>#cube</code> matches the name (of an object) in the 3D file/scene then draw a line from the enduser(&rsquo;s heart) to that object (to highlight it).</li>
<li>IF a <code>#cube</code> matches anything else in the XR Word Graph (XRWG) draw wires to them (text or related objects).</li>
</ol>
2023-09-07 14:06:50 +02:00
2023-12-06 16:40:55 +01:00
<h1 id="embedding-xr-content-using-src">Embedding XR content using src</h1>
2023-09-07 14:06:50 +02:00
2023-09-18 11:03:18 +02:00
<p><code>src</code> is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br>
2024-05-10 13:41:20 +02:00
It instances content (in objects) in the current scene/asset, and follows similar logic like the previous chapter, except that it does not modify the camera.</p>
2023-09-07 14:06:50 +02:00
<table>
<thead>
<tr>
2023-09-18 11:03:18 +02:00
<th>fragment</th>
<th>type</th>
<th>example value</th>
2023-09-07 14:06:50 +02:00
</tr>
</thead>
<tbody>
<tr>
2023-09-18 11:03:18 +02:00
<td><code>src</code></td>
2023-12-06 16:40:55 +01:00
<td>string (uri, hashtag/filter)</td>
<td><code>#cube</code><br><code>#sometag</code><br>#cube&amp;-ball_inside_cube<code>&lt;br&gt;</code>#-sky&amp;-rain<code>&lt;br&gt;</code>#-language&amp;english<code>&lt;br&gt;</code>#price=&gt;5<code>&lt;br&gt;</code><a href="https://linux.org/penguin.png`">https://linux.org/penguin.png`</a><br><code>https://linux.world/distrowatch.gltf#t=1,100</code><br><code>linuxapp://conference/nixworkshop/apply.gltf#-cta&amp;cta_apply</code><br><code>androidapp://page1?tutorial#pos=0,0,1&amp;t1,100</code><br><code>foo.mp3#0,0,0</code></td>
2023-09-07 14:06:50 +02:00
</tr>
2023-09-18 11:03:18 +02:00
</tbody>
</table>
2023-12-06 16:40:55 +01:00
<p>Here&rsquo;s an ascii representation of a 3D scene-graph with 3D objects <code></code> which embeds remote &amp; local 3D objects <code></code> with/out using filters:</p>
2023-09-21 13:05:30 +02:00
<pre><code> +────────────────────────────────────────────────────────+ +─────────────────────────+
│ │ │ │
│ index.gltf │ │ ocean.com/aquarium.fbx │
2023-12-06 16:40:55 +01:00
│ │ │ │ ├ room │
2023-09-21 13:05:30 +02:00
│ ├── ◻ canvas │ │ └── ◻ fishbowl │
│ │ └ src: painting.png │ │ ├─ ◻ bass │
│ │ │ │ └─ ◻ tuna │
│ ├── ◻ aquariumcube │ │ │
2023-12-06 16:40:55 +01:00
│ │ └ src: ://rescue.com/fish.gltf#fishbowl │ +─────────────────────────+
2023-09-21 13:05:30 +02:00
│ │ │
│ ├── ◻ bedroom │
│ │ └ src: #canvas │
│ │ │
│ └── ◻ livingroom │
│ └ src: #canvas │
│ │
+────────────────────────────────────────────────────────+
</code></pre>
<p>An XR Fragment-compatible browser viewing this scene, lazy-loads and projects <code>painting.png</code> onto the (plane) object called <code>canvas</code> (which is copy-instanced in the bed and livingroom).<br>
2023-12-06 16:40:55 +01:00
Also, after lazy-loading <code>ocean.com/aquarium.gltf</code>, only the queried objects <code>fishbowl</code> (and <code>bass</code> and <code>tuna</code>) will be instanced inside <code>aquariumcube</code>.<br>
2023-09-21 13:05:30 +02:00
Resizing will be happen accordingly to its placeholder object <code>aquariumcube</code>, see chapter Scaling.<br></p>
<blockquote>
2023-12-06 16:40:55 +01:00
<p>Instead of cherrypicking a rootobject <code>#fishbowl</code> with <code>src</code>, additional filters can be used to include/exclude certain objects. See next chapter on filtering below.</p>
2023-09-21 13:05:30 +02:00
</blockquote>
<p><strong>Specification</strong>:</p>
2023-09-07 14:06:50 +02:00
2023-09-18 11:03:18 +02:00
<ol>
2023-12-06 16:40:55 +01:00
<li>local/remote content is instanced by the <code>src</code> (filter) value (and attaches it to the placeholder mesh containing the <code>src</code> property)</li>
<li>by default all objects are loaded into the instanced src (scene) object (but not shown yet)</li>
<li><b>local</b> <code>src</code> values (<code>#...</code> e.g.) starting with a non-negating filter (<code>#cube</code> e.g.) will (deep)reparent that object (with name <code>cube</code>) as the new root of the scene at position 0,0,0</li>
<li><b>local</b> <code>src</code> values should respect (negative) filters (<code>#-foo&amp;price=&gt;3</code>)</li>
2023-09-18 11:03:18 +02:00
<li>the instanced scene (from a <code>src</code> value) should be <b>scaled accordingly</b> to its placeholder object or <b>scaled relatively</b> based on the scale-property (of a geometry-less placeholder, an &lsquo;empty&rsquo;-object in blender e.g.). For more info see Chapter Scaling.</li>
2023-09-22 10:08:28 +02:00
<li><b>external</b> <code>src</code> values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are:</li>
<li><code>src</code> values should make its placeholder object invisible, and only flush its children when the resolved content can succesfully be retrieved (see <a href="#links">broken links</a>)</li>
<li><b>external</b> <code>src</code> values should respect the fallback link mechanism (see <a href="#broken-links">broken links</a></li>
2023-09-18 11:03:18 +02:00
<li>when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer.</li>
2023-09-21 13:05:30 +02:00
<li>src-values are non-recursive: when linking to an external object (<code>src: foo.fbx#bar</code>), then <code>src</code>-metadata on object <code>bar</code> should be ignored.</li>
2023-12-06 16:40:55 +01:00
<li>an external <code>src</code>-value should always allow a sourceportation icon within 3 meter: teleporting to the origin URI to which the object belongs.</li>
2023-09-18 11:03:18 +02:00
<li>when only one object was cherrypicked (<code>#cube</code> e.g.), set its position to <code>0,0,0</code></li>
2023-12-06 16:40:55 +01:00
<li>when the enduser clicks an href with <code>#t=1,0,0</code> (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.)</li>
<li>a non-euclidian portal can be rendered for flat 3D objects (using stencil buffer e.g.) in case ofspatial <code>src</code>-values (an object <code>#world3</code> or URL <code>world3.fbx</code> e.g.).</li>
2023-09-18 11:03:18 +02:00
</ol>
<ul>
2023-12-06 16:40:55 +01:00
<li><code>model/gltf-binary</code></li>
2023-09-18 11:03:18 +02:00
<li><code>model/gltf+json</code></li>
<li><code>image/png</code></li>
<li><code>image/jpg</code></li>
2023-12-06 16:40:55 +01:00
<li><code>text/plain;charset=utf-8</code></li>
2023-09-18 11:03:18 +02:00
</ul>
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js">» example implementation</a><br>
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192">» example 3D asset</a><br>
<a href="https://github.com/coderofsalvation/xrfragment/issues/4">» discussion</a><br></p>
2023-12-06 16:40:55 +01:00
<h1 id="navigating-content-href-portals">Navigating content href portals</h1>
2023-09-18 11:03:18 +02:00
<p>navigation, portals &amp; mutations</p>
<table>
<thead>
2023-09-07 14:06:50 +02:00
<tr>
2023-09-18 11:03:18 +02:00
<th>fragment</th>
<th>type</th>
<th>example value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>href</code></td>
<td>string (uri or predefined view)</td>
<td><code>#pos=1,1,0</code><br><code>#pos=1,1,0&amp;rot=90,0,0</code><br><code>://somefile.gltf#pos=1,1,0</code><br></td>
2023-09-07 14:06:50 +02:00
</tr>
</tbody>
</table>
2023-09-18 11:03:18 +02:00
<ol>
2023-10-12 17:04:46 +02:00
<li><p>clicking an outbound &ldquo;external&rdquo;- or &ldquo;file URI&rdquo; fully replaces the current scene and assumes <code>pos=0,0,0&amp;rot=0,0,0</code> by default (unless specified)</p></li>
2023-09-18 11:03:18 +02:00
<li><p>relocation/reorientation should happen locally for local URI&rsquo;s (<code>#pos=....</code>)</p></li>
2024-04-25 21:06:03 +02:00
<li><p>navigation should not happen &ldquo;immediately&rdquo; when user is more than 5 meter away from the portal/object containing the href (to prevent accidental navigation e.g.)</p></li>
2023-09-18 11:03:18 +02:00
2024-04-25 21:06:03 +02:00
<li><p>URL navigation should always be reflected in the client URL-bar (in case of javascript: see [<a href="https://github.com/coderofsalvation/xrfragment/blob/dev/src/3rd/js/three/navigator.js">here</a> for an example navigator), and only update the URL-bar after the scene (default fragment <code>#</code>) has been loaded.</p></li>
2023-09-18 11:03:18 +02:00
2024-04-25 21:06:03 +02:00
<li><p>In immersive XR mode, the navigator back/forward-buttons should be always visible (using a wearable e.g., see [<a href="https://github.com/coderofsalvation/xrfragment/blob/dev/example/aframe/sandbox/index.html#L26-L29">here</a> for an example wearable)</p></li>
2023-09-18 11:03:18 +02:00
2024-04-25 21:06:03 +02:00
<li><p>make sure that the &ldquo;back-button&rdquo; of the &ldquo;browser-history&rdquo; always refers to the previous position (see [<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js#L97">here</a>)</p></li>
2023-09-18 11:03:18 +02:00
2024-04-25 21:06:03 +02:00
<li><p>ignore previous rule in special cases, like clicking an <code>href</code> using camera-portal collision (the back-button could cause a teleport-loop if the previous position is too close)</p></li>
2024-02-08 14:05:44 +01:00
2024-04-25 21:06:03 +02:00
<li><p>href-events should bubble upward the node-tree (from children to ancestors, so that ancestors can also contain an href), however only 1 href can be executed at the same time.</p></li>
2024-02-16 17:34:32 +01:00
2024-04-25 21:06:03 +02:00
<li><p>the end-user navigator back/forward buttons should repeat a back/forward action until a <code>pos=...</code> primitive is found (the stateless xrf:// href-values should not be pushed to the url-history)</p></li>
2023-09-18 11:03:18 +02:00
</ol>
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js">» example implementation</a><br>
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192">» example 3D asset</a><br>
<a href="https://github.com/coderofsalvation/xrfragment/issues/1">» discussion</a><br></p>
2023-12-06 16:40:55 +01:00
<h2 id="walking-surfaces">Walking surfaces</h2>
<p>XR Fragment-compatible viewers can infer this data based scanning the scene for:</p>
<ol>
<li>materialless (nameless &amp; textureless) mesh-objects (without <code>src</code> and <code>href</code>)</li>
</ol>
<blockquote>
<p>optionally the viewer can offer thumbstick, mouse or joystick teleport-tools for non-roomscale VR/AR setups.</p>
</blockquote>
2023-09-21 13:05:30 +02:00
<h2 id="ux-spec">UX spec</h2>
<p>End-users should always have read/write access to:</p>
<ol>
<li>the current (toplevel) <b>URL</b> (an URLbar etc)</li>
<li>URL-history (a <b>back/forward</b> button e.g.)</li>
<li>Clicking/Touching an <code>href</code> navigates (and updates the URL) to another scene/file (and coordinate e.g. in case the URL contains XR Fragments).</li>
</ol>
2023-09-18 11:03:18 +02:00
<h2 id="scaling-instanced-content">Scaling instanced content</h2>
2023-09-21 13:05:30 +02:00
<p>Sometimes embedded properties (like <code>src</code>) instance new objects.<br>
2023-09-18 11:03:18 +02:00
But what about their scale?<br>
How does the scale of the object (with the embedded properties) impact the scale of the referenced content?<br></p>
<blockquote>
<p>Rule of thumb: visible placeholder objects act as a &lsquo;3D canvas&rsquo; for the referenced scene (a plane acts like a 2D canvas for images e, a cube as a 3D canvas e.g.).</p>
</blockquote>
<ol>
<li><b>IF</b> an embedded property (<code>src</code> e.g.) is set on an non-empty placeholder object (geometry of &gt;2 vertices):</li>
</ol>
<ul>
<li>calculate the <b>bounding box</b> of the &ldquo;placeholder&rdquo; object (maxsize=1.4 e.g.)</li>
<li>hide the &ldquo;placeholder&rdquo; object (material e.g.)</li>
<li>instance the <code>src</code> scene as a child of the existing object</li>
<li>calculate the <b>bounding box</b> of the instanced scene, and scale it accordingly (to 1.4 e.g.)</li>
</ul>
<blockquote>
<p>REASON: non-empty placeholder object can act as a protective bounding-box (for remote content of which might grow over time e.g.)</p>
</blockquote>
<ol start="2">
2023-09-21 13:05:30 +02:00
<li>ELSE multiply the scale-vector of the instanced scene with the scale-vector (a common property of a 3D node) of the <b>placeholder</b> object.</li>
2023-09-18 11:03:18 +02:00
</ol>
<blockquote>
<p>TODO: needs intermediate visuals to make things more obvious</p>
</blockquote>
2023-12-06 16:40:55 +01:00
<h1 id="xr-fragment-pos">XR Fragment: pos</h1>
2024-06-17 07:59:51 +02:00
<p>[[» example implementation|<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js]">https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js]</a>]<br></p>
2023-12-06 16:40:55 +01:00
2024-06-17 07:59:51 +02:00
<h1 id="xr-fragment-rot">XR Fragment: rot</h1>
2023-12-06 16:40:55 +01:00
2024-06-17 07:59:51 +02:00
<p>[[» example implementation|<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js]">https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/pos.js]</a>]<br></p>
2023-12-06 16:40:55 +01:00
2024-06-17 07:59:51 +02:00
<h1 id="xr-fragment-t">XR Fragment: t</h1>
2023-12-06 16:40:55 +01:00
2024-06-17 07:59:51 +02:00
<p>[[» example implementation|<a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/t.js]">https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/t.js]</a>]<br></p>
2023-12-06 16:40:55 +01:00
<h1 id="xr-audio-video-integration">XR audio/video integration</h1>
<p>To play global audio/video items:</p>
<ol>
<li>add a <code>src: foo.mp3</code> or <code>src: bar.mp4</code> metadata to a 3D object (<code>cube</code> e.g.)</li>
2024-06-17 07:59:51 +02:00
<li>to enable auto-play and global timeline ([[#t=|t]]) control: hardcode a [[#t=|t]] XR Fragment: (<code>src: bar.mp3#t=0&amp;loop</code> e.g.)</li>
2023-12-06 16:40:55 +01:00
<li>to play it, add <code>href: #cube</code> somewhere else</li>
2024-06-17 07:59:51 +02:00
<li>to enable enduser-triggered play, use a [[URI Template]] XR Fragment: (<code>src: bar.mp3#{player}</code> and <code>play: t=0&amp;loop</code> and <code>href: xrf://#player=play</code> e.g.)</li>
<li>when the enduser clicks the <code>href</code>, <code>#t=0&amp;loop</code> (play) will be applied to the <code>src</code> value</li>
2023-12-06 16:40:55 +01:00
</ol>
<blockquote>
<p>NOTE: hardcoded framestart/framestop uses sampleRate/fps of embedded audio/video, otherwise the global fps applies. For more info see [[#t|t]].</p>
</blockquote>
<h1 id="xr-fragment-filters">XR Fragment filters</h1>
2023-09-21 13:05:30 +02:00
<p>Include, exclude, hide/shows objects using space-separated strings:</p>
<table>
<thead>
<tr>
<th>example</th>
<th>outcome</th>
</tr>
</thead>
<tbody>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>#-sky</code></td>
2023-09-21 13:05:30 +02:00
<td>show everything except object named <code>sky</code></td>
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>#-language&amp;english</code></td>
2023-09-21 13:05:30 +02:00
<td>hide everything with tag <code>language</code>, but show all tag <code>english</code> objects</td>
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>#-price&amp;price=&gt;10</code></td>
<td>hide all objects with property <code>price</code>, then only show object with price above 10</td>
2023-09-21 13:05:30 +02:00
</tr>
2024-06-19 14:13:15 +02:00
<tr>
<td><code>#-house*</code></td>
<td>hide <code>house</code> object and everything inside (=<code>*</code>)</td>
</tr>
2023-09-21 13:05:30 +02:00
</tbody>
</table>
<p>It&rsquo;s simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling:</p>
<ol>
2023-12-06 16:40:55 +01:00
<li>filters are a way to traverse a scene, and filter objects based on their name, tag- or property-values.</li>
2023-09-21 13:05:30 +02:00
</ol>
<ul>
2023-12-06 16:40:55 +01:00
<li>see <a href="https://coderofsalvation.github.io/xrfragment.media/queries.mp4">an (outdated) example video here</a> which used a dedicated <code>q=</code> variable (now deprecated and usable directly)</li>
2023-09-21 13:05:30 +02:00
</ul>
<h2 id="including-excluding">including/excluding</h2>
2023-12-06 16:40:55 +01:00
<p>By default, selectors work like photoshop-layers: they scan for matching layer(name/properties) within the scene-graph.
Each matched object (not their children) will be toggled (in)visible when selecting.</p>
2023-09-21 13:05:30 +02:00
<table>
<thead>
<tr>
<th>operator</th>
<th>info</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-</code></td>
2023-12-06 16:40:55 +01:00
<td>hides object(s) (<code>#-myobject&amp;-objects</code> e.g.</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>=</code></td>
<td>indicates an object-embedded custom property key/value (<code>#price=4&amp;category=foo</code> e.g.)</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>=&gt;</code> <code>=&lt;</code></td>
<td>compare float or int number (<code>#price=&gt;4</code> e.g.)</td>
2023-09-21 13:05:30 +02:00
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td><code>*</code></td>
<td>deepselect: automatically select children of selected object, including local (nonremote) embedded objects (starting with <code>#</code>)</td>
2023-09-21 13:05:30 +02:00
</tr>
</tbody>
</table>
<blockquote>
2023-12-06 16:40:55 +01:00
<p>NOTE 1: after an external embedded object has been instanced (<code>src: https://y.com/bar.fbx#room</code> e.g.), filters do not affect them anymore (reason: local tag/name collisions can be mitigated easily, but not in case of remote content).</p>
<p>NOTE 2: depending on the used 3D framework, toggling objects (in)visible should happen by enabling/disableing writing to the colorbuffer (to allow children being still visible while their parents are invisible).</p>
2023-09-21 13:05:30 +02:00
</blockquote>
<p><a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js">» example implementation</a>
2023-12-06 16:40:55 +01:00
<a href="https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/filter.gltf#L192">» example 3D asset</a>
2023-09-21 13:05:30 +02:00
<a href="https://github.com/coderofsalvation/xrfragment/issues/3">» discussion</a></p>
2023-12-06 16:40:55 +01:00
<h2 id="filter-parser">Filter Parser</h2>
2023-09-21 13:05:30 +02:00
2023-12-06 16:40:55 +01:00
<p>Here&rsquo;s how to write a filter parser:</p>
2023-09-21 13:05:30 +02:00
<ol>
2023-12-06 16:40:55 +01:00
<li>create an associative array/object to store filter-arguments as objects</li>
<li>detect object id&rsquo;s &amp; properties <code>foo=1</code> and <code>foo</code> (reference regex= <code>~/^.*=[&gt;&lt;=]?/</code> )</li>
<li>detect excluders like <code>-foo</code>,<code>-foo=1</code>,<code>-.foo</code>,<code>-/foo</code> (reference regex= <code>/^-/</code> )</li>
<li>detect root selectors like <code>/foo</code> (reference regex= <code>/^[-]?\//</code> )</li>
<li>detect number values like <code>foo=1</code> (reference regex= <code>/^[0-9\.]+$/</code> )</li>
<li>detect operators so you can easily strip keys (reference regex= <code>/(^-|\*$)/</code> )</li>
<li>detect exclude keys like <code>-foo</code> (reference regex= <code>/^-/</code> )</li>
<li>for every filter token split string on <code>=</code></li>
2023-09-21 13:05:30 +02:00
<li>and we set <code>root</code> to <code>true</code> or <code>false</code> (true=<code>/</code> root selector is present)</li>
2023-12-06 16:40:55 +01:00
<li>therefore we we set <code>show</code> to <code>true</code> or <code>false</code> (false=excluder <code>-</code>)</li>
2023-09-21 13:05:30 +02:00
</ol>
<blockquote>
2023-12-06 16:40:55 +01:00
<p>An example filter-parser (which compiles to many languages) can be <a href="https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Filter.hx">found here</a></p>
2023-09-21 13:05:30 +02:00
</blockquote>
<h1 id="visible-links">Visible links</h1>
2023-09-21 13:30:14 +02:00
<p>When predefined views, XRWG fragments and ID fragments (<code>#cube</code> or <code>#mytag</code> e.g.) are triggered by the enduser (via toplevel URL or clicking <code>href</code>):</p>
2023-09-21 13:05:30 +02:00
<ol>
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that ID (objectname)</li>
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) matching that <code>tag</code> value</li>
<li>draw a wire from the enduser (preferabbly a bit below the camera, heartposition) to object(s) containing that in their <code>src</code> or <code>href</code> value</li>
</ol>
2023-09-21 13:30:14 +02:00
<p>The obvious approach for this, is to consult the XRWG (<a href="https://github.com/coderofsalvation/xrfragment/blob/feat/macros/src/3rd/js/XRWG.js">example</a>), which basically has all these things already collected/organized for you during scene-load.</p>
<p><strong>UX</strong></p>
<ol start="4">
<li>do not update the wires when the enduser moves, leave them as is</li>
<li>offer a control near the back/forward button which allows the user to (turn off) control the correlation-intensity of the XRWG</li>
</ol>
2023-09-21 13:05:30 +02:00
2023-09-04 21:20:59 +02:00
<h1 id="text-in-xr-tagging-linking-to-spatial-objects">Text in XR (tagging,linking to spatial objects)</h1>
2023-09-18 11:03:18 +02:00
<p>How does XR Fragments interlink text with objects?</p>
2023-09-02 21:44:57 +02:00
2023-09-18 11:03:18 +02:00
<blockquote>
2023-09-21 13:30:14 +02:00
<p>The XR Fragments does this by collapsing space into a <strong>Word Graph</strong> (the <strong>XRWG</strong> <a href="https://github.com/coderofsalvation/xrfragment/blob/feat/macros/src/3rd/js/XRWG.js">example</a>), augmented by Bib(s)Tex.</p>
2023-09-18 11:03:18 +02:00
</blockquote>
2023-09-21 13:05:30 +02:00
<p>Instead of just throwing together all kinds media types into one experience (games), what about their tagged/semantical relationships?<br>
2024-04-25 21:06:03 +02:00
Perhaps the following question is related: why is HTML adopted less in games outside the browser?</p>
2023-09-18 11:03:18 +02:00
<p>Hence:</p>
2023-09-04 21:20:59 +02:00
<ol>
2024-04-25 21:06:03 +02:00
<li>XR Fragments promotes (de)serializing a scene to a (lowercase) XRWG (<a href="https://github.com/coderofsalvation/xrfragment/blob/feat/macros/src/3rd/js/XRWG.js">example</a>)</li>
2023-09-18 11:03:18 +02:00
<li>XR Fragments primes the XRWG, by collecting words from the <code>tag</code> and name-property of 3D objects.</li>
<li>XR Fragments primes the XRWG, by collecting words from <strong>optional</strong> metadata <strong>at the end of content</strong> of text (see default mimetype &amp; Data URI)</li>
2024-07-12 15:12:40 +02:00
<li>XR Fragments primes the XRWG, by collecting tags/id&rsquo;s from linked hypermedia (URI fragments for HTML e.g.)</li>
2023-09-18 11:03:18 +02:00
<li>The XRWG should be recalculated when textvalues (in <code>src</code>) change</li>
2024-04-25 21:06:03 +02:00
<li>HTML/RDF/JSON is still great, but is beyond the XRWG-scope (they fit better in the application-layer, or as embedded src content)</li>
2023-09-18 11:03:18 +02:00
<li>Applications don&rsquo;t have to be able to access the XRWG programmatically, as they can easily generate one themselves by traversing the scene-nodes.</li>
<li>The XR Fragment focuses on fast and easy-to-generate end-user controllable word graphs (instead of complex implementations that try to defeat word ambiguity)</li>
2024-07-12 15:12:40 +02:00
<li>Instead of exact lowercase word-matching, levensteihn-distance-based matching is preferred</li>
2023-09-04 21:20:59 +02:00
</ol>
2024-07-12 15:12:40 +02:00
<p>Example of generating XRWG out of the XRWG and textdata with hashtags:</p>
2023-09-06 15:43:29 +02:00
2024-07-12 15:12:40 +02:00
<pre><code> http://y.io/z.fbx | Derived XRWG (expressed as JSON)
2023-09-18 11:03:18 +02:00
----------------------------------------------------------------------------+--------------------------------------
2024-07-12 15:12:40 +02:00
| Chapter: ['#mydoc']
+-[src: data:.....]----------------------+ +-[3D mesh]-+ | one: ['#mydoc']
| Chapter one | | / \ | | houses: ['#castle','#mydoc','#house']
| | | / \ | | baroque: ['#mydoc','#castle']
| John built houses in baroque style. | | / \ | | castle: ['#baroque','#house']
| | | |_____| | | john: ['#john','#mydoc']
| | +-----│-----+ | mydoc: ['#mydoc']
2023-09-18 11:03:18 +02:00
| | │ |
| | ├─ name: castle |
| | └─ tag: house baroque |
+----------------------------------------+ |
2024-07-12 15:12:40 +02:00
└─ name: mydoc [3D mesh-+ |
2023-09-18 11:03:18 +02:00
| O ├─ name: john |
| /|\ | |
2024-07-12 15:12:40 +02:00
| / \ | | ^ ^ ^
+--------+ | | | |
|
[remotestorage.io]+ [ localstorage]-+ | &lt;- the XR Fragment-compatible
| XRWG (JSON) | | XRWG (JSON | | &lt;- 3D hypermedia viewer should
| | | | | &lt;- be able to select the active XRWG
+-----------------+ +---------------+ |
2023-09-04 21:20:59 +02:00
</code></pre>
2024-07-12 15:12:40 +02:00
<p>This allows hasslefree authoring and copy-paste of associations <strong>for and by humans</strong>, but also makes these URLs possible:</p>
2023-09-05 19:14:10 +02:00
<table>
<thead>
<tr>
2023-09-18 11:03:18 +02:00
<th>URL example</th>
<th>Result</th>
2023-09-05 19:14:10 +02:00
</tr>
</thead>
<tbody>
<tr>
2023-09-21 13:05:30 +02:00
<td><code>https://my.com/foo.gltf#baroque</code></td>
2024-07-12 15:12:40 +02:00
<td>draws lines between 3D mesh <code>castle</code>, and <code>mydoc</code>&rsquo;s text <code>baroque</code></td>
2023-09-05 19:14:10 +02:00
</tr>
<tr>
2023-09-18 11:03:18 +02:00
<td><code>https://my.com/foo.gltf#john</code></td>
2024-07-12 15:12:40 +02:00
<td>draws lines between mesh <code>john</code>, and the text <code>John</code> of <code>mydoc</code></td>
2023-09-05 19:14:10 +02:00
</tr>
<tr>
2023-09-18 11:03:18 +02:00
<td><code>https://my.com/foo.gltf#house</code></td>
2023-09-21 13:05:30 +02:00
<td>draws lines between mesh <code>castle</code>, and other objects with tag <code>house</code> or <code>todo</code></td>
2023-09-05 19:14:10 +02:00
</tr>
</tbody>
</table>
2023-09-02 21:44:57 +02:00
2023-09-09 11:30:03 +02:00
<blockquote>
2024-07-12 15:12:40 +02:00
<p>the URI fragment <code>#john&amp;mydoc&amp;house</code> would draw a connection between these 3 meshes.</p>
2023-09-09 11:30:03 +02:00
</blockquote>
2024-07-12 15:12:40 +02:00
<p>The XRWG allows endusers to show/hide relationships in realtime in XR Browsers at various levels:</p>
2023-09-18 11:03:18 +02:00
<ul>
<li>wordmatch <strong>inside</strong> <code>src</code> text</li>
<li>wordmatch <strong>inside</strong> <code>href</code> text</li>
<li>wordmatch object-names</li>
<li>wordmatch object-tagnames</li>
</ul>
2023-09-21 13:05:30 +02:00
<p>Spatial wires can be rendered between words/objects etc.<br>
2023-09-18 11:03:18 +02:00
Some pointers for good UX (but not necessary to be XR Fragment compatible):</p>
<ol start="9">
2023-09-06 15:13:36 +02:00
<li>The XR Browser needs to adjust tag-scope based on the endusers needs/focus (infinite tagging only makes sense when environment is scaled down significantly)</li>
<li>The XR Browser should always allow the human to view/edit the metadata, by clicking &lsquo;toggle metadata&rsquo; on the &lsquo;back&rsquo; (contextmenu e.g.) of any XR text, anywhere anytime.</li>
2023-09-18 11:03:18 +02:00
<li>respect multi-line BiBTeX metadata in text because of <a href="#core-principle">the core principle</a></li>
<li>Default font (unless specified otherwise) is a modern monospace font, for maximized tabular expressiveness (see <a href="#core-principle">the core principle</a>).</li>
<li>anti-pattern: hardcoupling an XR Browser with a mandatory <strong>markup/scripting-language</strong> which departs from onubtrusive plain text (HTML/VRML/Javascript) (see <a href="#core-principle">the core principle</a>)</li>
<li>anti-pattern: limiting human introspection, by abandoning plain text as first tag citizen.</li>
2023-09-04 21:20:59 +02:00
</ol>
2023-09-02 21:44:57 +02:00
<h2 id="default-data-uri-mimetype">Default Data URI mimetype</h2>
2023-09-04 21:20:59 +02:00
<p>The <code>src</code>-values work as expected (respecting mime-types), however:</p>
2024-02-08 14:05:44 +01:00
<p>The XR Fragment specification advices to bump the traditional default browser-mimetype</p>
2023-09-02 21:44:57 +02:00
<p><code>text/plain;charset=US-ASCII</code></p>
2024-07-12 15:12:40 +02:00
<p>to a hashtag-friendly one:</p>
2023-09-02 21:44:57 +02:00
2024-07-12 15:12:40 +02:00
<p><code>text/plain;charset=utf-8;hashtag</code></p>
2023-09-02 21:44:57 +02:00
2023-09-08 17:01:14 +02:00
<p>This indicates that:</p>
2023-09-02 21:44:57 +02:00
2023-09-04 21:20:59 +02:00
<ul>
2023-09-08 17:01:14 +02:00
<li>utf-8 is supported by default</li>
2024-07-12 15:12:40 +02:00
<li>words beginning with <code>#</code> (hashtags) will prime the XRWG by adding the hashtag to the XRWG, linking to the current sentence/paragraph/alltext (depending on &lsquo;.&rsquo;) to the XRWG</li>
2023-09-04 21:20:59 +02:00
</ul>
2023-09-08 17:01:14 +02:00
<p>Advantages:</p>
2023-09-04 21:20:59 +02:00
<ul>
2023-09-08 17:01:14 +02:00
<li>out-of-the-box (de)multiplex human text and metadata in one go (see <a href="#core-principle">the core principle</a>)</li>
2023-09-06 15:13:36 +02:00
<li>no network-overhead for metadata (see <a href="#core-principle">the core principle</a>)</li>
2024-07-12 15:12:40 +02:00
<li>ensuring high FPS: realtime HTML/RDF historically is too &lsquo;requesty&rsquo;/&lsquo;parsy&rsquo; for game studios</li>
2023-09-04 21:20:59 +02:00
<li>rich send/receive/copy-paste everywhere by default, metadata being retained (see <a href="#core-principle">the core principle</a>)</li>
2023-09-06 15:13:36 +02:00
<li>netto result: less webservices, therefore less servers, and overall better FPS in XR</li>
2023-09-04 21:20:59 +02:00
</ul>
<blockquote>
2023-09-06 15:13:36 +02:00
<p>This significantly expands expressiveness and portability of human tagged text, by <strong>postponing machine-concerns to the end of the human text</strong> in contrast to literal interweaving of content and markupsymbols (or extra network requests, webservices e.g.).</p>
2023-09-04 21:20:59 +02:00
</blockquote>
2023-09-08 17:01:14 +02:00
<p>For all other purposes, regular mimetypes can be used (but are not required by the spec).<br></p>
2023-09-04 21:20:59 +02:00
<h2 id="url-and-data-uri">URL and Data URI</h2>
2023-09-02 21:44:57 +02:00
<pre><code> +--------------------------------------------------------------+ +------------------------+
| | | author.com/article.txt |
| index.gltf | +------------------------+
| │ | | |
2024-07-12 18:19:19 +02:00
| ├── ◻ article_canvas | | Hello #friends |
2023-09-02 21:44:57 +02:00
| │ └ src: ://author.com/article.txt | | |
2024-07-12 18:19:19 +02:00
| │ | +------------------------+
| └── ◻ note_canvas |
| └ src:`data:welcome human\n@book{sunday...}` |
| |
2023-09-02 21:44:57 +02:00
| |
+--------------------------------------------------------------+
</code></pre>
2023-09-18 11:03:18 +02:00
<p>The enduser will only see <code>welcome human</code> and <code>Hello friends</code> rendered verbatim (see mimetype).
2023-09-09 11:30:03 +02:00
The beauty is that text in Data URI automatically promotes rich copy-paste (retaining metadata).
2023-09-04 21:20:59 +02:00
In both cases, the text gets rendered immediately (onto a plane geometry, hence the name &lsquo;_canvas&rsquo;).
The XR Fragment-compatible browser can let the enduser access visual-meta(data)-fields after interacting with the object (contextmenu e.g.).</p>
2023-09-07 14:06:50 +02:00
<blockquote>
2023-09-18 11:03:18 +02:00
<p>additional tagging using <a href="https://github.com/coderofsalvation/hashtagbibs">bibs</a>: to tag spatial object <code>note_canvas</code> with &lsquo;todo&rsquo;, the enduser can type or speak <code>#note_canvas@todo</code></p>
2023-09-05 19:14:10 +02:00
</blockquote>
2023-09-04 21:20:59 +02:00
2024-07-12 15:12:40 +02:00
<h1 id="importing-exporting">Importing/exporting</h1>
2023-09-08 17:01:14 +02:00
2024-07-12 15:12:40 +02:00
<p>For usecases like importing/exporting/p2p casting a scene, the issue of external files comes into play.</p>
2023-09-06 15:13:36 +02:00
2024-07-12 15:12:40 +02:00
<ol>
<li>export: if the 3D scene contains relative src/href values, rewrite them into absolute URL values.</li>
</ol>
2023-09-08 17:01:14 +02:00
2024-07-12 18:19:19 +02:00
<h1 id="reflection-mapping">Reflection Mapping</h1>
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
<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>
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
<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>
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
<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.
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
# Transclusion (broken link) resolution
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
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>+────────────────────────────────────────────────────────+
2023-09-22 10:08:28 +02:00
│ │
│ index.gltf │
│ │ │
2024-02-08 14:05:44 +01:00
│ │ #: #-offlinetext │
2023-09-22 10:08:28 +02:00
│ │ │
│ ├── ◻ buttonA │
2024-07-12 18:19:19 +02:00
│ │ └ href: <a href="http://foo.io/campagne.fbx">http://foo.io/campagne.fbx</a>
2023-10-12 17:04:46 +02:00
│ │ └ href@404: ipfs://foo.io/campagne.fbx │
2024-02-08 14:05:44 +01:00
│ │ └ href@400: #clienterrortext │
2023-09-22 10:08:28 +02:00
│ │ └ ◻ offlinetext │
│ │ │
2024-07-12 18:19:19 +02:00
│ └── ◻ 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.
2023-09-22 10:08:28 +02:00
│ │
2024-07-12 18:19:19 +02:00
+────────────────────────────────────────────────────────+</p>
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
<pre><code>
# Topic-based index-less Webrings
2023-09-22 10:08:28 +02:00
2024-07-12 18:19:19 +02:00
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:
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
* `href: schoolA.edu/projects.gltf#math`
* `href: schoolB.edu/projects.gltf#math`
* `href: university.edu/projects.gltf#math`
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
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:
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
* `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`
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
&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
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
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.
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
# URI Templates (RFC6570)
2023-10-12 17:04:46 +02:00
2024-07-12 18:19:19 +02:00
XR Fragments adopts Level1 URI **Fragment** expansion to provide safe interactivity.&lt;br&gt;
The following demonstrates a simple video player:
2023-09-01 14:20:02 +02:00
2024-07-12 18:19:19 +02:00
</code></pre>
2023-09-04 21:20:59 +02:00
2024-07-12 18:19:19 +02:00
<p>+─────────────────────────────────────────────+
2024-02-08 14:05:44 +01:00
│ │
2024-07-12 18:19:19 +02:00
│ foo.usdz │<br>
│ │ │<br>
│ │ │<br>
2024-02-08 14:05:44 +01:00
│ ├── ◻ stopbutton │
│ │ ├ #: #-stopbutton │
│ │ └ href: #player=stop&amp;-stopbutton │ (stop and hide stop-button)
2024-07-12 18:19:19 +02:00
│ │ │<br>
2024-02-08 14:05:44 +01:00
│ └── ◻ plane │
│ ├ play: #t=l:0,10 │
│ ├ stop: #t=0,0 │
│ ├ href: #player=play&amp;stopbutton │ (play and show stop-button)
│ └ src: cat.mp4#{player} │
│ │
│ │
2024-07-12 18:19:19 +02:00
+─────────────────────────────────────────────+</p>
2024-02-08 14:05:44 +01:00
2024-07-12 18:19:19 +02:00
<p>&rdquo;`</p>
2024-02-08 14:05:44 +01:00
2024-02-29 15:07:20 +01:00
<h1 id="additional-scene-metadata">Additional scene metadata</h1>
2024-04-25 21:26:22 +02:00
<p>XR Fragments does not aim to redefine the metadata-space or accessibility-space by introducing its own cataloging-metadata fields.
2024-02-29 15:07:20 +01:00
Instead, it encourages browsers to scan nodes for the following custom properties:</p>
<ul>
<li><a href="https://spdx.dev/">SPDX</a> license information</li>
<li><a href="https://www.w3.org/WAI/standards-guidelines/aria/">ARIA</a> attributes (<code>aria-*: .....</code>)</li>
<li><a href="https://ogp.me">Open Graph</a> attributes (<code>og:*: .....</code>)</li>
<li><a href="https://www.dublincore.org/specifications/dublin-core/application-profile-guidelines/">Dublin-Core</a> attributes(<code>dc:*: .....</code>)</li>
<li><a href="https://bibtex.eu/fields">BibTex</a> when known bibtex-keys exist with values enclosed in <code>{</code> and <code>},</code></li>
</ul>
<p><strong>ARIA</strong> (<code>aria-description</code>) is the most important to support, as it promotes accessibility and allows scene transcripts. Please start <code>aria-description</code> with a verb to aid transcripts.</p>
<blockquote>
<p>Example: object &lsquo;tryceratops&rsquo; with <code>aria-description: is a huge dinosaurus standing on a #mountain</code> generates transcript <code>#tryceratops is a huge dinosaurus standing on a #mountain</code>, where the hashtags are clickable XR Fragments (activating the visible-links in the XR browser).</p>
</blockquote>
<p>Individual nodes can be enriched with such metadata, but most importantly the scene node:</p>
<table>
<thead>
<tr>
<th>metadata key</th>
<th>example value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>aria-description</code>, <code>og:description</code>, <code>dc:description</code></td>
<td><code>An immersive experience about Triceratops</code> (*)</td>
</tr>
<tr>
<td><code>SPDX</code></td>
<td><code>CC0-1.0</code></td>
</tr>
<tr>
<td><code>dc:creator</code></td>
<td><code>John Doe</code></td>
</tr>
<tr>
<td><code>dc:title</code>, <code>og:title</code></td>
<td>&lsquo;Triceratops` (*)</td>
</tr>
<tr>
<td><code>og:site_name</code></td>
<td><code>https://xrfragment.org</code></td>
</tr>
<tr>
<td><code>dc.publisher</code></td>
<td><code>NLNET</code></td>
</tr>
<tr>
<td><code>dc.date</code></td>
<td><code>2024-01-01</code></td>
</tr>
<tr>
<td><code>dc.identifier</code></td>
<td><code>XRFRAGMENT-001</code></td>
</tr>
<tr>
<td><code>journal</code> (bibTeX)</td>
<td><code>{Future Of Text Vol 3},</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>* = these are interchangable (only one needs to be defined)</p>
</blockquote>
<p>There&rsquo;s no silver bullet when it comes to metadata, so one should support where the metadata is/goes.</p>
<blockquote>
<p>These attributes can be scanned and presented during an <code>href</code> or <code>src</code> eye/mouse-over.</p>
</blockquote>
2024-04-25 21:26:22 +02:00
<h1 id="accessibility-interface">Accessibility interface</h1>
<p>The addressibility of XR Fragments allows for unique 3D-to-text transcripts, as well as an textual interface to navigate 3D content.<br>
Spec:<br><Br></p>
<ol>
<li>The enduser must be able to enable an accessibility-mode (which persists across application/webpage restarts)</li>
<li>Accessibility-mode must contain a text-input for the user to enter text</li>
<li>Accessibility-mode must contain a flexible textlog for the user to read (via screenreader, screen, or TTS e.g.)</li>
2024-06-17 15:58:13 +02:00
<li>the textlog contains <code>aria-descriptions</code>, and its narration (Screenreader e.g.) can be skipped (via 2-button navigation)</li>
2024-04-25 21:26:22 +02:00
<li>The <code>back</code> command should navigate back to the previous URL (alias for browser-backbutton)</li>
<li>The <code>forward</code> command should navigate back to the next URL (alias for browser-nextbutton)</li>
<li>A destination is a 3D node containing an <code>href</code> with a <code>pos=</code> XR fragment</li>
<li>The <code>go</code> command should list all possible destinations</li>
<li>The <code>go left</code> command should move the camera around 0.3 meters to the left</li>
<li>The <code>go right</code> command should move the camera around 0.3 meters to the right</li>
<li>The <code>go forward</code> command should move the camera 0.3 meters forward (direction of current rotation).</li>
<li>The <code>rotate left</code> command should rotate the camera 0.3 to the left</li>
<li>The <code>rotate left</code> command should rotate the camera 0.3 to the right</li>
<li>The (dynamic) <code>go abc</code> command should navigate to <code>#pos=scene2</code> in case there&rsquo;s a 3D node with name <code>abc</code> and <code>href</code> value <code>#pos=scene2</code></li>
<li>The <code>look</code> command should give an (contextual) 3D-to-text transcript, by scanning the <code>aria-description</code> values of the current <code>pos=</code> value (including its children)</li>
<li>The <code>do</code> command should list all possible <code>href</code> values which don&rsquo;t contain an <code>pos=</code> XR Fragment</li>
<li>The (dynamic) <code>do abc</code> command should navigate/execute <code>https://.../...</code> in case a 3D node exist with name <code>abc</code> and <code>href</code> value <code>https://.../...</code></li>
</ol>
2024-06-17 15:58:13 +02:00
<h2 id="two-button-navigation">Two-button navigation</h2>
<p>For specific user-profiles, gyroscope/mouse/keyboard/audio/visuals will not be available.<br>
Therefore a 2-button navigation-interface is the bare minimum interface:</p>
<ol>
<li>objects with href metadata can be cycled via a key (tab on a keyboard)</li>
<li>objects with href metadata can be activated via a key (enter on a keyboard)</li>
<li>the TTS reads the href-value (and/or aria-description if available)</li>
</ol>
2024-02-08 14:05:44 +01:00
<h1 id="security-considerations">Security Considerations</h1>
<p>The only dynamic parts are <a href="https://www.w3.org/TR/media-frags/">W3C Media Fragments</a> and <a href="https://www.rfc-editor.org/rfc/rfc6570">URI Templates (RFC6570)</a>.<br>
The use of URI Templates is limited to pre-defined variables and Level0 fragments-expansion only, which makes it quite safe.<br>
In fact, it is much safer than relying on a scripting language (javascript) which can change URN too.</p>
2023-09-01 14:20:02 +02:00
2023-09-21 13:05:30 +02:00
<h1 id="faq">FAQ</h1>
<p><strong>Q:</strong> Why is everything HTTP GET-based, what about POST/PUT/DELETE HATEOS<br>
<strong>A:</strong> Because it&rsquo;s out of scope: XR Fragment specifies a read-only way to surf XR documents. These things belong in the application layer (for example, an XR Hypermedia browser can decide to support POST/PUT/DELETE requests for embedded HTML thru <code>src</code> values)</p>
<hr>
2024-02-08 14:05:44 +01:00
<p><strong>Q:</strong> Why isn&rsquo;t there support for scripting, URI Template Fragments are so limited compared to WASM &amp; javascript
<strong>A:</strong> This is out of scope as it unhyperifies hypermedia, and this is up to XR hypermedia browser-extensions.<br> Historically scripting/Javascript seems to been able to turn webpages from hypermedia documents into its opposite (hyperscripted nonhypermedia documents).<br>In order to prevent this backward-movement (hypermedia tends to liberate people from finnicky scripting) XR Fragment uses <a href="https://www.w3.org/TR/media-frags/">W3C Media Fragments</a> and <a href="https://www.rfc-editor.org/rfc/rfc6570">URI Templates (RFC6570)</a>, to prevent unhyperifying itself by hardcoupling to a particular markup or scripting language. <br>
2023-10-12 17:04:46 +02:00
XR Fragments supports filtering objects in a scene only, because in the history of the javascript-powered web, showing/hiding document-entities seems to be one of the most popular basic usecases.<br>
2024-02-08 14:05:44 +01:00
Doing advanced scripting &amp; networkrequests under the hood are obviously interesting endavours, but this is something which should not be hardcoupled with XR Fragments or hypermedia.<br>This perhaps belongs more to browser extensions.<br>
2023-10-12 17:04:46 +02:00
Non-HTML Hypermedia browsers should make browser extensions the right place, to &lsquo;extend&rsquo; experiences, in contrast to code/javascript inside hypermedia documents (this turned out as a hypermedia antipattern).</p>
2023-09-21 13:05:30 +02:00
2024-02-08 14:05:44 +01:00
<h1 id="authors">authors</h1>
<ul>
<li>Leon van Kammen (@lvk@mastodon.online)</li>
<li>Jens Finkhäuser (@jens@social.finkhaeuser.de)</li>
</ul>
2023-09-01 14:20:02 +02:00
<h1 id="iana-considerations">IANA Considerations</h1>
<p>This document has no IANA actions.</p>
<h1 id="acknowledgments">Acknowledgments</h1>
2023-09-07 15:53:32 +02:00
<ul>
<li><a href="https://nlnet.nl">NLNET</a></li>
<li><a href="https://futureoftext.org">Future of Text</a></li>
<li><a href="https://visual-meta.info">visual-meta.info</a></li>
2023-12-06 16:40:55 +01:00
<li>Michiel Leenaars</li>
<li>Gerben van der Broeke</li>
<li>Mauve</li>
<li>Jens Finkhäuser</li>
<li>Marc Belmont</li>
<li>Tim Gerritsen</li>
<li>Frode Hegland</li>
<li>Brandel Zackernuk</li>
<li>Mark Anderson</li>
2023-09-07 15:53:32 +02:00
</ul>
<h1 id="appendix-definitions">Appendix: Definitions</h1>
<table>
<thead>
<tr>
<th>definition</th>
<th>explanation</th>
</tr>
</thead>
<tbody>
<tr>
<td>human</td>
<td>a sentient being who thinks fuzzy, absorbs, and shares thought (by plain text, not markuplanguage)</td>
</tr>
<tr>
<td>scene</td>
<td>a (local/remote) 3D scene or 3D file (index.gltf e.g.)</td>
</tr>
<tr>
<td>3D object</td>
<td>an object inside a scene characterized by vertex-, face- and customproperty data.</td>
</tr>
2024-01-30 10:58:00 +01:00
<tr>
<td>URI</td>
<td>some resource at something somewhere via someprotocol (<code>http://me.com/foo.glb#foo</code> or <code>e76f8efec8efce98e6f</code> <a href="https://interpeer.io">see interpeer.io</a>)</td>
</tr>
<tr>
<td>URL</td>
<td>something somewhere via someprotocol (<code>http://me.com/foo.glb</code>)</td>
</tr>
2024-02-08 14:05:44 +01:00
<tr>
<td>URN</td>
<td>something at some domain (<code>me.com/foo.glb</code>)</td>
</tr>
2023-09-07 15:53:32 +02:00
<tr>
<td>metadata</td>
<td>custom properties of text, 3D Scene or Object(nodes), relevant to machines and a human minority (academics/developers)</td>
</tr>
<tr>
<td>XR fragment</td>
<td>URI Fragment with spatial hints like <code>#pos=0,0,0&amp;t=1,100</code> e.g.</td>
</tr>
2023-09-18 11:03:18 +02:00
<tr>
<td>the XRWG</td>
<td>wordgraph (collapses 3D scene to tags)</td>
</tr>
<tr>
<td>the hashbus</td>
<td>hashtags map to camera/scene-projections</td>
</tr>
<tr>
<td>spacetime hashtags</td>
<td>positions camera, triggers scene-preset/time</td>
</tr>
2023-09-21 13:05:30 +02:00
<tr>
<td>teleportation</td>
<td>repositioning the enduser to a different position (or 3D scene/file)</td>
</tr>
<tr>
<td>sourceportation</td>
<td>teleporting the enduser to the original XR Document of an <code>src</code> embedded object.</td>
</tr>
2023-09-18 11:03:18 +02:00
<tr>
<td>placeholder object</td>
<td>a 3D object which with src-metadata (which will be replaced by the src-data.)</td>
</tr>
2023-09-07 15:53:32 +02:00
<tr>
<td>src</td>
<td>(HTML-piggybacked) metadata of a 3D object which instances content</td>
</tr>
<tr>
<td>href</td>
<td>(HTML-piggybacked) metadata of a 3D object which links to content</td>
</tr>
<tr>
2023-12-06 16:40:55 +01:00
<td>filter</td>
<td>URI Fragment(s) which show/hide object(s) in a scene based on name/tag/property (<code>#cube&amp;-price=&gt;3</code>)</td>
2023-09-07 15:53:32 +02:00
</tr>
<tr>
<td>visual-meta</td>
<td><a href="https://visual.meta.info">visual-meta</a> data appended to text/books/papers which is indirectly visible/editable in XR.</td>
</tr>
<tr>
<td>requestless metadata</td>
<td>metadata which never spawns new requests (unlike RDF/HTML, which can cause framerate-dropping, hence not used a lot in games)</td>
</tr>
<tr>
<td>FPS</td>
<td>frames per second in spatial experiences (games,VR,AR e.g.), should be as high as possible</td>
</tr>
<tr>
<td>introspective</td>
<td>inward sensemaking (&ldquo;I feel this belongs to that&rdquo;)</td>
</tr>
<tr>
<td>extrospective</td>
<td>outward sensemaking (&ldquo;I&rsquo;m fairly sure John is a person who lives in oklahoma&rdquo;)</td>
</tr>
<tr>
<td><code></code></td>
<td>ascii representation of an 3D object/mesh</td>
</tr>
<tr>
<td>(un)obtrusive</td>
<td>obtrusive: wrapping human text/thought in XML/HTML/JSON obfuscates human text into a salad of machine-symbols and words</td>
</tr>
2023-12-06 16:40:55 +01:00
<tr>
<td>flat 3D object</td>
<td>a 3D object of which all verticies share a plane</td>
</tr>
2023-09-07 15:53:32 +02:00
<tr>
<td>BibTeX</td>
<td>simple tagging/citing/referencing standard for plaintext</td>
</tr>
<tr>
<td>BibTag</td>
<td>a BibTeX tag</td>
</tr>
2023-09-08 17:01:14 +02:00
<tr>
<td>(hashtag)bibs</td>
2023-09-18 11:03:18 +02:00
<td>an easy to speak/type/scan tagging SDL (<a href="https://github.com/coderofsalvation/hashtagbibs">see here</a> which expands to BibTex/JSON/XML</td>
2023-09-08 17:01:14 +02:00
</tr>
2023-09-07 15:53:32 +02:00
</tbody>
</table>
2023-09-01 14:20:02 +02:00
</section>
</body>
</html>