From dd2434a3ed74e88c91fdc33a9e5486763db23f40 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Fri, 10 May 2024 13:41:20 +0200 Subject: [PATCH] feat/godot wip --- .gitignore | 3 +- doc/RF6_XR_Fragments.png | Bin 28764 -> 28757 bytes doc/RFC_XR_Fragments.html | 8 +- doc/RFC_XR_Fragments.md | 14 ++- doc/RFC_XR_Fragments.txt | 206 +++++++++++++++--------------- doc/RFC_XR_Fragments.xml | 8 +- doc/RFC_XR_Macros.txt | 30 ++--- example/godot/index.glb | 1 - example/godot/main.gd | 32 +++-- example/godot/project.godot | 2 +- example/godot/xrfragment.gd | 234 ++++++++++++++++++++++++++++++++++- make | 7 +- src/xrfragment/xrfragment.gd | 222 +-------------------------------- 13 files changed, 405 insertions(+), 362 deletions(-) delete mode 120000 example/godot/index.glb mode change 120000 => 100644 example/godot/xrfragment.gd mode change 100644 => 120000 src/xrfragment/xrfragment.gd 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 006922e7291c8d7a695de00f6f93db8c40b39302..8188c86dec7ffb55a24e96bf5412b8bcbd78926a 100644 GIT binary patch literal 28757 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 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

    +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

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