diff --git a/.gitignore b/.gitignore index 6d90f51..38a8853 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ tags example/assets/*.blend* 2wa.gitlab.io/* src/3rd/js/aframe/build/aframe - +example/godot/addons +example/godot/dist diff --git a/doc/RF6_XR_Fragments.png b/doc/RF6_XR_Fragments.png index 006922e..8188c86 100644 Binary files a/doc/RF6_XR_Fragments.png and b/doc/RF6_XR_Fragments.png differ diff --git a/doc/RFC_XR_Fragments.html b/doc/RFC_XR_Fragments.html index 4f81486..e2c916d 100644 --- a/doc/RFC_XR_Fragments.html +++ b/doc/RFC_XR_Fragments.html @@ -720,7 +720,11 @@ For example, to render a portal with a preview-version of the scene, create an 3
  • set the position of the camera accordingly to the vector3 values of #pos
  • rot sets the rotation of the camera (only for non-VR/AR headsets)
  • mediafragment t in the top-URL sets the playbackspeed and animation-range of the global scene animation
  • -
  • after scene load: in case an href does not mention any pos-coordinate, pos=0,0,0 will be assumed
  • +
  • before scene load: the scene is cleared
  • +
  • after scene load: in case the scene (rootnode) contains an # default view with a fragment value: execute non-positional fragments via the hashbus (no top-level URL change)
  • +
  • after scene load: in case the scene (rootnode) contains an # default view with a fragment value: execute positional fragment via the hashbus + update top-level URL
  • +
  • in case of no default # view on the scene (rootnode), default player(rig) position 0,0,0 is assumed.
  • +
  • in case a href does not mention any pos-coordinate, the current position will be assumed
  • Here’s an ascii representation of a 3D scene-graph which contains 3D objects and their metadata:

    @@ -761,7 +765,7 @@ In case of buttonA the end-user will be teleported to another locat

    Embedding XR content using src

    src is the 3D version of the iframe.
    -It instances content (in objects) in the current scene/asset.

    +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.

    diff --git a/doc/RFC_XR_Fragments.md b/doc/RFC_XR_Fragments.md index d16e33d..2db971d 100644 --- a/doc/RFC_XR_Fragments.md +++ b/doc/RFC_XR_Fragments.md @@ -383,10 +383,14 @@ Example URI's: [» discussion](https://github.com/coderofsalvation/xrfragment/issues/5)
    1. the Y-coordinate of `pos` 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). -1. set the position of the camera accordingly to the vector3 values of `#pos` -1. `rot` sets the rotation of the camera (only for non-VR/AR headsets) -1. mediafragment `t` in the top-URL sets the playbackspeed and animation-range of the global scene animation -1. after scene load: in case an `href` does not mention any `pos`-coordinate, `pos=0,0,0` will be assumed +2. set the position of the camera accordingly to the vector3 values of `#pos` +3. `rot` sets the rotation of the camera (only for non-VR/AR headsets) +4. mediafragment `t` in the top-URL sets the playbackspeed and animation-range of the global scene animation +5. before scene load: the scene is cleared +6. after scene load: in case the scene (rootnode) contains an `#` default view with a fragment value: execute non-positional fragments via the hashbus (no top-level URL change) +7. after scene load: in case the scene (rootnode) contains an `#` default view with a fragment value: execute positional fragment via the hashbus + update top-level URL +8. in case of no default `#` view on the scene (rootnode), default player(rig) position `0,0,0` is assumed. +9. in case a `href` does not mention any `pos`-coordinate, the current position will be assumed Here's an ascii representation of a 3D scene-graph which contains 3D objects `◻` and their metadata: @@ -424,7 +428,7 @@ The URL-processing-flow for hypermedia browsers goes like this: # Embedding XR content using src `src` is the 3D version of the iframe.
    -It instances content (in objects) in the current scene/asset. +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. | fragment | type | example value | |----------|------|---------------| diff --git a/doc/RFC_XR_Fragments.txt b/doc/RFC_XR_Fragments.txt index 3444f6e..cea33b4 100644 --- a/doc/RFC_XR_Fragments.txt +++ b/doc/RFC_XR_Fragments.txt @@ -3,7 +3,7 @@ Jens & Leon Internet Engineering Task Force L.R. van Kammen -Internet-Draft 25 April 2024 +Internet-Draft 9 May 2024 Intended status: Informational @@ -46,16 +46,16 @@ Status of This Memo time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." - This Internet-Draft will expire on 27 October 2024. + This Internet-Draft will expire on 10 November 2024. -van Kammen Expires 27 October 2024 [Page 1] +van Kammen Expires 10 November 2024 [Page 1] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 Copyright Notice @@ -109,9 +109,9 @@ Table of Contents -van Kammen Expires 27 October 2024 [Page 2] +van Kammen Expires 10 November 2024 [Page 2] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 21. Additional scene metadata . . . . . . . . . . . . . . . . . . 35 @@ -165,9 +165,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 3] +van Kammen Expires 10 November 2024 [Page 3] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 XR Fragments tries to seek to connect the world of text (semantical @@ -221,9 +221,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 4] +van Kammen Expires 10 November 2024 [Page 4] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +───────────────────────────────────────────────────────────────────────────────────────────────+ @@ -277,9 +277,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 5] +van Kammen Expires 10 November 2024 [Page 5] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * allowing 3D assets/nodes to publish XR Fragments to themselves/ @@ -333,9 +333,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 6] +van Kammen Expires 10 November 2024 [Page 6] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +=========+======================+=====================================+ @@ -389,9 +389,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 7] +van Kammen Expires 10 November 2024 [Page 7] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 4. Conventions and Definitions @@ -445,9 +445,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 8] +van Kammen Expires 10 November 2024 [Page 8] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 my.io/scene.fbx @@ -501,9 +501,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 9] +van Kammen Expires 10 November 2024 [Page 9] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 |Media Fragments |media fragment |#t=0,2&loop |play (and | @@ -557,9 +557,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 10] +van Kammen Expires 10 November 2024 [Page 10] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 6.2. Fragment-to-metadata mapping @@ -613,9 +613,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 11] +van Kammen Expires 10 November 2024 [Page 11] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 | | | | |metadata (bar:#t=0 | @@ -669,9 +669,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 12] +van Kammen Expires 10 November 2024 [Page 12] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 | temporal | s=x | 1 | set playback | @@ -725,9 +725,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 13] +van Kammen Expires 10 November 2024 [Page 13] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 | | | | uv | @@ -781,9 +781,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 14] +van Kammen Expires 10 November 2024 [Page 14] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +──────────────────────────────────────────────────────────+ @@ -837,9 +837,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 15] +van Kammen Expires 10 November 2024 [Page 15] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 1. the Y-coordinate of pos identifies the floorposition. This means @@ -850,8 +850,17 @@ Internet-Draft XR Fragments April 2024 3. rot sets the rotation of the camera (only for non-VR/AR headsets) 4. mediafragment t in the top-URL sets the playbackspeed and animation-range of the global scene animation - 5. after scene load: in case an href does not mention any pos- - coordinate, pos=0,0,0 will be assumed + 5. before scene load: the scene is cleared + 6. after scene load: in case the scene (rootnode) contains an # + default view with a fragment value: execute non-positional + fragments via the hashbus (no top-level URL change) + 7. after scene load: in case the scene (rootnode) contains an # + default view with a fragment value: execute positional fragment + via the hashbus + update top-level URL + 8. in case of no default # view on the scene (rootnode), default + player(rig) position 0,0,0 is assumed. + 9. in case a href does not mention any pos-coordinate, the current + position will be assumed Here's an ascii representation of a 3D scene-graph which contains 3D objects ◻ and their metadata: @@ -881,23 +890,20 @@ Internet-Draft XR Fragments April 2024 The URL-processing-flow for hypermedia browsers goes like this: + + + +van Kammen Expires 10 November 2024 [Page 16] + +Internet-Draft XR Fragments May 2024 + + 1. IF a #cube matches a custom property-key (of an object) in the 3D file/scene (#cube: #......) THEN execute that predefined_view. 2. IF scene operators (pos) and/or animation operator (t) are present in the URL then (re)position the camera and/or animation- range accordingly. - - - - - - -van Kammen Expires 27 October 2024 [Page 16] - -Internet-Draft XR Fragments April 2024 - - 3. IF no camera-position has been set in step 1 or 2 update the top-level URL with #pos=0,0,0 (example (https://github.com/co derofsalvation/xrfragment/blob/main/src/3rd/js/three/ @@ -912,7 +918,9 @@ Internet-Draft XR Fragments April 2024 src is the 3D version of the iframe. - It instances content (in objects) in the current scene/asset. + 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. +========+========+===================================================+ |fragment|type |example value | @@ -941,17 +949,9 @@ Internet-Draft XR Fragments April 2024 - - - - - - - - -van Kammen Expires 27 October 2024 [Page 17] +van Kammen Expires 10 November 2024 [Page 17] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +────────────────────────────────────────────────────────+ +─────────────────────────+ @@ -1005,9 +1005,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 18] +van Kammen Expires 10 November 2024 [Page 18] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 6. external src values should be served with appropriate @@ -1061,9 +1061,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 19] +van Kammen Expires 10 November 2024 [Page 19] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +==========+==================+============================+ @@ -1117,9 +1117,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 20] +van Kammen Expires 10 November 2024 [Page 20] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 » example implementation @@ -1173,9 +1173,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 21] +van Kammen Expires 10 November 2024 [Page 21] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * calculate the bounding box of the instanced scene, and @@ -1229,9 +1229,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 22] +van Kammen Expires 10 November 2024 [Page 22] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 14. XR audio/video integration @@ -1285,9 +1285,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 23] +van Kammen Expires 10 November 2024 [Page 23] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 15.1. including/excluding @@ -1341,9 +1341,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 24] +van Kammen Expires 10 November 2024 [Page 24] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 2. detect object id's & properties foo=1 and foo (reference regex= @@ -1397,9 +1397,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 25] +van Kammen Expires 10 November 2024 [Page 25] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 | The XR Fragments does this by collapsing space into a *Word Graph* @@ -1453,9 +1453,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 26] +van Kammen Expires 10 November 2024 [Page 26] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 http://y.io/z.fbx | Derived XRWG (expressed as BibTex) @@ -1509,9 +1509,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 27] +van Kammen Expires 10 November 2024 [Page 27] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 As seen above, the XRWG can expand bibs @@ -1565,9 +1565,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 28] +van Kammen Expires 10 November 2024 [Page 28] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 12. Default font (unless specified otherwise) is a modern monospace @@ -1621,9 +1621,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 29] +van Kammen Expires 10 November 2024 [Page 29] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * lines beginning with @ will not be rendered verbatim by default @@ -1677,9 +1677,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 30] +van Kammen Expires 10 November 2024 [Page 30] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +--------------------------------------------------------------+ +------------------------+ @@ -1733,9 +1733,9 @@ xrtext = { -van Kammen Expires 27 October 2024 [Page 31] +van Kammen Expires 10 November 2024 [Page 31] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 // bibtex: ↓@ ↓ ↓property ↓end @@ -1789,9 +1789,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 32] +van Kammen Expires 10 November 2024 [Page 32] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 str = ` @@ -1845,9 +1845,9 @@ console.log( xrtext.encode(text,tags) ) // multiplex text & bibtex back to -van Kammen Expires 27 October 2024 [Page 33] +van Kammen Expires 10 November 2024 [Page 33] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 2. mirroring files on another protocol using (HTTP) errorcode tags @@ -1901,9 +1901,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 34] +van Kammen Expires 10 November 2024 [Page 34] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * href: university.edu/projects.gltf#math&-theme math @@ -1957,9 +1957,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 35] +van Kammen Expires 10 November 2024 [Page 35] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * BibTex (https://bibtex.eu/fields) when known bibtex-keys exist @@ -2013,9 +2013,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 36] +van Kammen Expires 10 November 2024 [Page 36] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 22. Accessibility interface @@ -2069,9 +2069,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 37] +van Kammen Expires 10 November 2024 [Page 37] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 (javascript) which can change URN too. @@ -2125,9 +2125,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 38] +van Kammen Expires 10 November 2024 [Page 38] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 * visual-meta.info (https://visual-meta.info) @@ -2181,9 +2181,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 39] +van Kammen Expires 10 November 2024 [Page 39] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 | hashtags | time | @@ -2237,9 +2237,9 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 40] +van Kammen Expires 10 November 2024 [Page 40] -Internet-Draft XR Fragments April 2024 +Internet-Draft XR Fragments May 2024 +-----------------+---------------------------------------------+ @@ -2293,4 +2293,4 @@ Internet-Draft XR Fragments April 2024 -van Kammen Expires 27 October 2024 [Page 41] +van Kammen Expires 10 November 2024 [Page 41] diff --git a/doc/RFC_XR_Fragments.xml b/doc/RFC_XR_Fragments.xml index c3fb0c3..e41a3a1 100644 --- a/doc/RFC_XR_Fragments.xml +++ b/doc/RFC_XR_Fragments.xml @@ -612,7 +612,11 @@ For example, to render a portal with a preview-version of the scene, create an 3
  • set the position of the camera accordingly to the vector3 values of #pos
  • rot sets the rotation of the camera (only for non-VR/AR headsets)
  • mediafragment t in the top-URL sets the playbackspeed and animation-range of the global scene animation
  • -
  • after scene load: in case an href does not mention any pos-coordinate, pos=0,0,0 will be assumed
  • +
  • before scene load: the scene is cleared
  • +
  • after scene load: in case the scene (rootnode) contains an # default view with a fragment value: execute non-positional fragments via the hashbus (no top-level URL change)
  • +
  • after scene load: in case the scene (rootnode) contains an # default view with a fragment value: execute positional fragment via the hashbus + update top-level URL
  • +
  • in case of no default # view on the scene (rootnode), default player(rig) position 0,0,0 is assumed.
  • +
  • in case a href does not mention any pos-coordinate, the current position will be assumed
  • Here's an ascii representation of a 3D scene-graph which contains 3D objects and their metadata: @@ -651,7 +655,7 @@ In case of buttonA the end-user will be teleported to another location
    Embedding XR content using src src is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.
    -It instances content (in objects) in the current scene/asset.
    +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.
    diff --git a/doc/RFC_XR_Macros.txt b/doc/RFC_XR_Macros.txt index 713dd45..dceb5da 100644 --- a/doc/RFC_XR_Macros.txt +++ b/doc/RFC_XR_Macros.txt @@ -3,7 +3,7 @@ Internet Engineering Task Force L.R. van Kammen -Internet-Draft 25 April 2024 +Internet-Draft 9 May 2024 Intended status: Informational @@ -38,7 +38,7 @@ Status of This Memo time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." - This Internet-Draft will expire on 27 October 2024. + This Internet-Draft will expire on 10 November 2024. Copyright Notice @@ -53,9 +53,9 @@ Copyright Notice -van Kammen Expires 27 October 2024 [Page 1] +van Kammen Expires 10 November 2024 [Page 1] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 extracted from this document must include Revised BSD License text as @@ -109,9 +109,9 @@ Table of Contents -van Kammen Expires 27 October 2024 [Page 2] +van Kammen Expires 10 November 2024 [Page 2] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 3. Metadata-values can contain the | symbol to 🎲 roundrobin variable @@ -165,9 +165,9 @@ Internet-Draft XR Macros April 2024 -van Kammen Expires 27 October 2024 [Page 3] +van Kammen Expires 10 November 2024 [Page 3] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 +=========+======+===================+=================+=============+ @@ -221,9 +221,9 @@ Internet-Draft XR Macros April 2024 -van Kammen Expires 27 October 2024 [Page 4] +van Kammen Expires 10 November 2024 [Page 4] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 Table 3 @@ -277,9 +277,9 @@ Internet-Draft XR Macros April 2024 -van Kammen Expires 27 October 2024 [Page 5] +van Kammen Expires 10 November 2024 [Page 5] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 4.5. Usecase: present context menu with options @@ -333,9 +333,9 @@ click object with (`!clickme`:`!foo|!bar|!flop` e.g.) -van Kammen Expires 27 October 2024 [Page 6] +van Kammen Expires 10 November 2024 [Page 6] -Internet-Draft XR Macros April 2024 +Internet-Draft XR Macros May 2024 | Note that only macro's can trigger roundrobin values or @@ -389,4 +389,4 @@ Internet-Draft XR Macros April 2024 -van Kammen Expires 27 October 2024 [Page 7] +van Kammen Expires 10 November 2024 [Page 7] diff --git a/example/godot/index.glb b/example/godot/index.glb deleted file mode 120000 index 0675c21..0000000 --- a/example/godot/index.glb +++ /dev/null @@ -1 +0,0 @@ -../assets/index.glb \ No newline at end of file diff --git a/example/godot/main.gd b/example/godot/main.gd index d1cbff4..aa97513 100644 --- a/example/godot/main.gd +++ b/example/godot/main.gd @@ -1,14 +1,32 @@ extends Node3D var xr_interface: XRInterface +var xrf +var scene +var player:CharacterBody3D func _ready(): - # Import MyClass - const XRF = preload("res://xrfragment.gd") - var xrf = XRF.new() + xrf = preload("res://xrfragment.gd").new() add_child(xrf) - print( xrf.parseURL("https://foo.com/?foo#flop=bar&fap=fop") ) - print( xrf.parseURL("https://foo.com/flop.html?foo#flop=bar&fap=fop") ) - xrf.load("https://xrfragment.org/index.glb") - + xrf.to("https://xrfragment.org/index.glb", _onXRF ) + player = find_child("PlayerBody") + player.enabled = false # optional: turn off gravity +func _onXRF(event:String,data ): + if event == "scene_loaded": + scene = data + setPredefinedSceneView() + +func _input(event): + if event is InputEventMouseMotion: + var mouse_sens = 0.2 + var cam = find_child("XRCamera3D") + cam.rotate_y(deg_to_rad(-event.relative.x*mouse_sens)) + +# info: https://xrfragment.org/#predefined_view +# spec: 6-8 @ https://xrfragment.org/doc/RFC_XR_Fragments.html#navigating-3d +func setPredefinedSceneView(): + var XRF = scene.get_meta("XRF") + if XRF && XRF.has("#") && XRF["#"]["fragment"]["pos"]: + player.teleport( xrf.posToTransform3D(XRF["#"]["fragment"]["pos"]) ) + diff --git a/example/godot/project.godot b/example/godot/project.godot index b1195af..ad910ca 100644 --- a/example/godot/project.godot +++ b/example/godot/project.godot @@ -21,7 +21,7 @@ XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd [editor_plugins] -enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg") +enabled=PackedStringArray() [rendering] diff --git a/example/godot/xrfragment.gd b/example/godot/xrfragment.gd deleted file mode 120000 index cb55d13..0000000 --- a/example/godot/xrfragment.gd +++ /dev/null @@ -1 +0,0 @@ -../../src/xrfragment/xrfragment.gd \ No newline at end of file diff --git a/example/godot/xrfragment.gd b/example/godot/xrfragment.gd new file mode 100644 index 0000000..f4ed7e7 --- /dev/null +++ b/example/godot/xrfragment.gd @@ -0,0 +1,233 @@ +# https://xrfragment.org" +# SPDX-License-Identifier: MPL-2.0" + +extends Node + +class_name XRF + +var scene +var isModelLoading = false +var metadata +var callback: Callable; +var Type = { + "isColor": "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$", + "isInt": "^[0-9]+$", + "isFloat": "^[0-9]+%.[0-9]+$", + "isVector": "([,]+|%w)" +} + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass + +#################################################################################################### +# URI Related functions +#################################################################################################### + +func parseURL(url: String) -> Dictionary: + var URI = {} + + # Split URL by '://' to get protocol and the rest of the URL + var parts = url.split("://") + if parts.size() > 1: + URI["protocol"] = parts[0] + url = parts[1] + else: + URI["protocol"] = "http" # Default to http if protocol is missing + + # Split URL by '/' to separate domain, path, and file + parts = url.split("/") + URI["domain"] = parts[0] + if parts.size() > 1: + var path_and_file = parts[1] + var path_and_file_parts = path_and_file.split("/") + if path_and_file_parts.size() > 1: + URI["path"] = path_and_file_parts + var file = path_and_file_parts.pop_back() + URI["path"] = path_and_file_parts.join("/") + else: + URI["path"] = path_and_file + + # Check if there's a query string + if url.find("?") != -1: + parts = url.split("?") + URI["path"] = parts[0] + var args = parts[1] + if args.find("#"): + args = args.split("#")[0] + URI["query"] = parseArgs(args) + else: + URI["query"] = {} + + # Check if there's a fragment + if url.find("#") != -1: + parts = url.split("#") + URI["fragment"] = parseArgs(parts[1]) + else: + URI["fragment"] = {} + + return URI + +func parseArgs(fragment: String) -> Dictionary: + var ARG = {} + + # Split fragment by '&' to separate items + var items = fragment.split("&") + + for item in items: + # Split item by '=' to separate key and value + var key_value = item.split("=") + if key_value.size() > 1: + ARG[key_value[0]] = guess_type(key_value[1]) + else: + ARG[key_value[0]] = "" + + return ARG + +func guess_type(str: String) -> Dictionary: + var v = { + "string": str, + "x": null, + "y": null, + "color":null, + "float": null, + "int": null + } + var parts = str.split(",") + if parts.size() > 1: + v.x = parts[0].to_int() + v.y = parts[1].to_int() + if parts.size() > 2: + v.z = parts[2].to_int() + if str.match(Type.isColor): + v.color = str + if str.match(Type.isFloat): + v.float = str.to_float() + if str.match(Type.isInt): + v.int = str.to_int() + return v + +#################################################################################################### +# Model Related functions +#################################################################################################### + +# Download model by HTTP and run `downloadModelSuccess` if OK +func to(url, f:Callable ): + print("loading "+url) + callback = f + var http_request = HTTPRequest.new() + add_child(http_request) + http_request.request_completed.connect(downloadModelSuccess) + + var error = http_request.request(url) + if error != OK: + push_error("An error occurred in the HTTP request.") + +func downloadModelSuccess(result, response_code, headers, body): + # TODO: here different parsing functions should be called + # based on the filetype (glb,gltf,ade,obj e.g.) + loadModelFromBufferByGLTFDocument(body) + _parseXRFMetadata(scene) + traverse( scene, _parseXRFMetadata ) + # setup actions & embeds + traverse( scene, init_href ) + traverse( scene, init_src ) + callback.call("scene_loaded", scene) + +func loadModelFromBufferByGLTFDocument(body): + print("loadModelFromBuffer") + var doc = GLTFDocument.new() + var state = GLTFState.new() + #state.set_handle_binary_image(GLTFState.HANDLE_BINARY_EMBED_AS_BASISU) # Fixed in new Godot version (4.3 as I see) https://github.com/godotengine/godot/blob/17e7f85c06366b427e5068c5b3e2940e27ff5f1d/scene/resources/portable_compressed_texture.cpp#L116 + var error = doc.append_from_buffer(body, "", state) + if error == OK: + scene = doc.generate_scene(state) + metadata = _parseMetadata(state,scene) + add_child(scene) + print("model added") + else: + print("Couldn't load glTF scene (error code: %s). Are you connected to internet?" % error_string(error)) + +func _parseXRFMetadata(node:Node): + if node.has_meta("extras"): + var extras = node.get_meta("extras") + var XRF = {} + for i in extras: + if typeof(extras[i]) == TYPE_STRING: + XRF[ i ] = parseURL( extras[i] ) + node.set_meta("XRF", XRF) + +func traverse(node, f:Callable ): + for N in node.get_children(): + if N.get_child_count() > 0: + f.call(N) + self.traverse(N,f) + else: + f.call(N) + +func _parseMetadata(state: GLTFState, scene: Node) -> Error: + #var meta = new Dictionary() + + # Add metadata to materials + var materials_json : Array = state.json.get("materials", []) + var materials : Array[Material] = state.get_materials() + for i in materials_json.size(): + if materials_json[i].has("extras"): + materials[i].set_meta("extras", materials_json[i]["extras"]) + + # Add metadata to ImporterMeshes + var meshes_json : Array = state.json.get("meshes", []) + var meshes : Array[GLTFMesh] = state.get_meshes() + for i in meshes_json.size(): + if meshes_json[i].has("extras"): + meshes[i].mesh.set_meta("extras", meshes_json[i]["extras"]) + + # Add metadata to scene + var scenes_json : Array = state.json.get("scenes", []) + if scenes_json[0].has("extras"): + scene.set_meta("extras", scenes_json[0]["extras"]) + + # Add metadata to nodes + var nodes_json : Array = state.json.get("nodes", []) + for i in nodes_json.size(): + if nodes_json[i].has("extras"): + var name = nodes_json[i]["name"].replace(".","_") + var node = scene.find_child(name) #state.get_scene_node(i) + if node: + node.set_meta( "extras", nodes_json[i]["extras"] ) + else: + print(name+" could not be found") + return OK + +func posToTransform3D(v:Dictionary): + var pos = Vector3() + if !v.x: + var node:Node3D = scene.find_child(v.string) + pos.x = node.position.x + pos.y = node.position.y + pos.z = node.position.z + else: + pos.x = v.x + pos.y = v.y + pos.z = v.z + var transform = Transform3D() + transform.origin = pos + return transform + + +#################################################################################################### +# The XR Fragments +# spec: https://xrfragment.org/doc/RFC_XR_Fragments.html +#################################################################################################### + +func init_href(node:Node): + null + +func init_src(node:Node): + null + diff --git a/make b/make index 36ea4d1..9a3e47c 100755 --- a/make +++ b/make @@ -27,9 +27,10 @@ install(){ } godot(){ - test -d src/xrfragment/godot/addons || mkdir src/xrfragment/godot/addons - test -d src/xrfragment/godot/addons/godot-xr-tools || { - cd src/xrfragment/godot + DIR_GODOT=example/godot + test -d $DIR_GODOT/addons || mkdir -p $DIR_GODOT/addons + test -d $DIR_GODOT/addons/godot-xr-tools || { + cd $DIR_GODOT wget "https://github.com/GodotVR/godot-xr-tools/releases/download/4.3.1/godot-xr-tools.zip" unzip godot-xr-tools.zip mv godot-xr-tools/addons/godot-xr-tools addons/. diff --git a/src/xrfragment/xrfragment.gd b/src/xrfragment/xrfragment.gd deleted file mode 100644 index 5bbe66c..0000000 --- a/src/xrfragment/xrfragment.gd +++ /dev/null @@ -1,221 +0,0 @@ -extends Node - -class_name XRF - -var rootScene -var isModelLoading = false - -# Called when the node enters the scene tree for the first time. -func _ready(): - pass # Replace with function body. - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass - -#################################################################################################### -# URI Related functions -#################################################################################################### - -func parseURL(url: String) -> Dictionary: - var URI = {} - - # Split URL by '://' to get protocol and the rest of the URL - var parts = url.split("://") - if parts.size() > 1: - URI["protocol"] = parts[0] - url = parts[1] - else: - URI["protocol"] = "http" # Default to http if protocol is missing - - # Split URL by '/' to separate domain, path, and file - parts = url.split("/") - URI["domain"] = parts[0] - if parts.size() > 1: - var path_and_file = parts[1] - var path_and_file_parts = path_and_file.split("/") - if path_and_file_parts.size() > 1: - URI["path"] = path_and_file_parts - var file = path_and_file_parts.pop_back() - URI["path"] = path_and_file_parts.join("/") - else: - URI["path"] = path_and_file - - # Check if there's a query string - if url.find("?") != -1: - parts = url.split("?") - URI["path"] = parts[0] - var args = parts[1] - if args.find("#"): - args = args.split("#")[0] - URI["query"] = parseArgs(args) - else: - URI["query"] = {} - - # Check if there's a fragment - if url.find("#") != -1: - parts = url.split("#") - URI["fragment"] = parseArgs(parts[1]) - else: - URI["fragment"] = {} - - return URI - -func parseArgs(fragment: String) -> Dictionary: - var ARG = {} - - # Split fragment by '&' to separate items - var items = fragment.split("&") - - for item in items: - # Split item by '=' to separate key and value - var key_value = item.split("=") - if key_value.size() > 1: - ARG[key_value[0]] = key_value[1] - else: - ARG[key_value[0]] = "" - - return ARG - -#################################################################################################### -# Model Related functions -#################################################################################################### - -# Download model by HTTP and run `downloadModelSuccess` if OK -func load(url): - print("loading "+url) - var http_request = HTTPRequest.new() - add_child(http_request) - http_request.request_completed.connect(downloadModelSuccess) - - var error = http_request.request(url) - if error != OK: - push_error("An error occurred in the HTTP request.") - - - -func placeModelToEditorScene(model): - add_child(model) - #model.translate(Vector3(0.0, 0.0, -1.0)) - print("model added") - -func downloadModelSuccess(result, response_code, headers, body): - # TODO: here different parsing functions should be called - # based on the filetype (glb,gltf,ade,obj e.g.) - loadModelFromBufferByGLTFDocument(body) - -func loadModelFromBufferByGLTFDocument(body): - print("loadModelFromBuffer") - var doc = GLTFDocument.new() - var state = GLTFState.new() - #state.set_handle_binary_image(GLTFState.HANDLE_BINARY_EMBED_AS_BASISU) # Fixed in new Godot version (4.3 as I see) https://github.com/godotengine/godot/blob/17e7f85c06366b427e5068c5b3e2940e27ff5f1d/scene/resources/portable_compressed_texture.cpp#L116 - - var error = doc.append_from_buffer(body, "", state) - if error == OK: - #var glb_importer_model: GLTFMesh = state.get_meshes()[0] - #var glb_importer_model_mesh: ImporterMesh = glb_importer_model.get_mesh() - var scene = doc.generate_scene(state) - scene.set_meta("json",state.json) - #var ok:Error = _parseExtras(state) - traverse(scene, evalNode) - self.placeModelToEditorScene(scene) - - else: - print("Couldn't load glTF scene (error code: %s)." % error_string(error)) - -func evalNode(node:Node): - print(node.name) - #if node.has_meta("extras"): - # print(node.get_meta("extras","")) - -func traverse(node, f:Callable ): - for N in node.get_children(): - if N.get_child_count() > 0: - f.call(N) - self.traverse(N,f) - else: - f.call(N) - -func _parseExtras(state: GLTFState) -> Error: - print( state.json.keys() ) - - # Add metadata to materials - var materials_json : Array = state.json.get("materials", []) - var materials : Array[Material] = state.get_materials() - for i in materials_json.size(): - if materials_json[i].has("extras"): - materials[i].set_meta("extras", materials_json[i]["extras"]) - - # Add metadata to ImporterMeshes - var meshes_json : Array = state.json.get("meshes", []) - var meshes : Array[GLTFMesh] = state.get_meshes() - for i in meshes_json.size(): - if meshes_json[i].has("extras"): - meshes[i].mesh.set_meta("extras", meshes_json[i]["extras"]) - - # Add metadata to nodes - var nodes_json : Array = state.json.get("nodes", []) - for i in nodes_json.size(): - var node = state.get_scene_node(i) - if !node: - continue - if nodes_json[i].has("extras"): - # Handle special case - if node is ImporterMeshInstance3D: - # ImporterMeshInstance3D nodes will be converted later to either - # MeshInstance3D or StaticBody3D and metadata will be lost - # A sibling is created preserving the metadata. It can be later - # merged back in using a EditorScenePostImport script - var metadata_node = Node.new() - metadata_node.set_meta("extras", nodes_json[i]["extras"]) - - # Meshes are also ImporterMeshes that will be later converted either - # to ArrayMesh or some form of collision shape. - # We'll save it as another metadata item. If the mesh is reused we'll - # have duplicated info but at least it will always be accurate - if node.mesh and node.mesh.has_meta("extras"): - metadata_node.set_meta("mesh_extras", node.mesh.get_meta("extras")) - - # Well add it as sibling so metadata node always follows the actual metadata owner - node.add_sibling(metadata_node) - # Make sure owner is set otherwise it won't get serialized to disk - metadata_node.owner = node.owner - # Add a suffix to the generated name so it's easy to find - metadata_node.name += "_meta" - # In all other cases just set_meta - else: - node.set_meta("extras", nodes_json[i]["extras"]) - - ## now we merge extras to the scene - #var verbose_output = [] - #var nodes : Array[Node] = scene.find_children("*" + "_meta", "Node") - #verbose_output.append_array(["Metadata nodes:", nodes]) - #for node in nodes: - #var extras = node.get_meta("extras") - #if !extras: - #verbose_output.append("Node %s contains no 'extras' metadata" % node) - #continue - #var parent = node.get_parent() - #if !parent: - #verbose_output.append("Node %s has no parent" % node) - #continue - #var idx_original = node.get_index() - 1 - #if idx_original < 0 or parent.get_child_count() <= idx_original: - #verbose_output.append("Original node index %s is out of bounds. Parent child count: %s" % [idx_original, parent.get_child_count()]) - #continue - #var original = node.get_parent().get_child(idx_original) - #if original: - #verbose_output.append("Setting extras metadata for %s" % original) - #original.set_meta("extras", extras) - #if node.has_meta("mesh_extras"): - #if original is MeshInstance3D and original.mesh: - #verbose_output.append("Setting extras metadata for mesh %s" % original.mesh) - #original.mesh.set_meta("extras", node.get_meta("mesh_extras")) - #else: - #verbose_output.append("Metadata node %s has 'mesh_extras' but original %s has no mesh, preserving as 'mesh_extras'" % [node, original]) - #original.set_meta("mesh_extras", node.get_meta("mesh_extras")) - #else: - #verbose_output.append("Original node not found for %s" % node) - #node.queue_free() - return OK diff --git a/src/xrfragment/xrfragment.gd b/src/xrfragment/xrfragment.gd new file mode 120000 index 0000000..2f8b4cd --- /dev/null +++ b/src/xrfragment/xrfragment.gd @@ -0,0 +1 @@ +../../example/godot/xrfragment.gd \ No newline at end of file