From a4b05c752de3d1f99e85a88086c16039cff153e4 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Fri, 9 Feb 2024 18:00:22 +0100 Subject: [PATCH] refactor media fragments: extensions do not conflict with vanilla media fragments --- doc/RFC_XR_Fragments.html | 98 ++++----- doc/RFC_XR_Fragments.md | 39 ++-- doc/RFC_XR_Fragments.txt | 248 +++++++++++------------ doc/RFC_XR_Fragments.xml | 94 ++++----- example/aframe/sandbox/index.html | 2 +- src/3rd/js/XRWG.js | 7 +- src/3rd/js/three/xrf/dynamic/material.js | 12 +- src/3rd/js/three/xrf/loop.js | 17 ++ src/3rd/js/three/xrf/s.js | 24 ++- src/3rd/js/three/xrf/src/audio.js | 44 ++-- src/3rd/js/three/xrf/src/video.js | 21 +- src/3rd/js/three/xrf/suv.js | 11 - src/3rd/js/three/xrf/t.js | 23 ++- src/3rd/js/three/xrf/uv.js | 56 +++-- src/3rd/js/three/xrf/xywh.js | 9 - src/Test.hx | 7 +- src/spec/suv.json | 4 - src/spec/uv.json | 3 +- src/spec/xywh.json | 4 - src/xrfragment/Parser.hx | 7 +- src/xrfragment/URI.hx | 3 +- src/xrfragment/XRF.hx | 13 +- 22 files changed, 376 insertions(+), 370 deletions(-) create mode 100644 src/3rd/js/three/xrf/loop.js delete mode 100644 src/3rd/js/three/xrf/suv.js delete mode 100644 src/3rd/js/three/xrf/xywh.js delete mode 100644 src/spec/suv.json delete mode 100644 src/spec/xywh.json diff --git a/doc/RFC_XR_Fragments.html b/doc/RFC_XR_Fragments.html index 340c480..6e2f70f 100644 --- a/doc/RFC_XR_Fragments.html +++ b/doc/RFC_XR_Fragments.html @@ -435,7 +435,7 @@ That way, if the link gets shared, the XR Fragments implementation at http -r + #soldout*=halfopacity set material of objects tagged with product to material with name metallic @@ -496,20 +496,6 @@ That way, if the link gets shared, the XR Fragments implementation at http play from 0 seconds till 2 seconds (and stop) - -temporal W3C media fragment * -t=[l:]x,y -l:0,1 -play [as loop] between x and y - - - -temporal W3C media fragment * -uv=u,v -uv:0,0,1,1 -set uv offset (default 0,0) - - temporal W3C media fragment * s=x @@ -519,9 +505,44 @@ That way, if the link gets shared, the XR Fragments implementation at http temporal W3C media fragment * -suv=[l:]uspeed,vspeed -uv:l:0.1,0.2 -set uv scroll speed of (default 1,1 is instant) [l: means infinite texturescrolling] otherwise new u,v values will be lerped to +[-]loop +loop +enable looped playback of audio/video/3D anim + + + + + +-loop +disable looped playback (does not affect playbackstate of media) + + + +vector2 +uv=uv,v,uspeed,vspeed +0,0 +set uv offset instantly (default speed = 1,1) + + + + + ++0.5,+0.5 +scroll instantly by adding 0.5 to the current uv coordinates + + + + + +0.2,1,0.1,0.1 +scroll (lerp) to uv coordinate 0,2,1 with 0.1 units per second + + + + + +0,0,0,+0.1 +scroll v coordinates with 0.1 units per second (infinitely) @@ -534,49 +555,16 @@ That way, if the link gets shared, the XR Fragments implementation at http
-

* = this is extending the W3C media fragments with finergrained playback/viewport-control:

-
+

* = this is extending the W3C media fragments with (missing) playback/viewport-control. Normally #t=0,2 implies setting start/stop-values AND starting playback, whereas #s=0&loop allows pausing a video, speeding up/slowing down media, as well as enabling/disabling looping.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
extensioninfo
l: specifices loopt=0,2 specifies oneshot-play (default) whereas t=l:0,2 indicates looped-play
#uv= specifies uv-coordinatesallows offsetting the uv-coordinates
#suv= specifies scrollspeed of uv-coordinatesallows single/infinite uv-scrolling
#s specifies playback speedbeing able to specify loop(speed) of audio/video
- -
-

The rationale not to extend the xywh-media fragment is that 3D geometries deal with triangular polygons (not rectangular).

+

The rationale for uv is that the xywh Media Fragment deals with rectangular media, which does not translate well to 3D models (which use triangular polygons, not rectangular) positioned by uv-coordinates.

Example URI’s:

    -
  • https://images.org/credits.jpg#t=0&suv=l:0,0.1 (infinite vertical texturescrolling)
  • -
  • https://video.org/organogram.mp4#t=0&suv:0.1,0.1&uv=0.3,0.3 (animated zoom towards region in video)
  • +
  • https://images.org/credits.jpg#uv=0,0,0,+0.1 (infinite vertical texturescrolling)
  • +
  • https://video.org/organogram.mp4#t=0&loop&uv:0.1,0.1,0.3,0.3 (animated zoom towards region in looped video)
  • https://shaders.org/plasma.glsl#t=0&u:col1=1,0,0&u:col2=0,1,0 (red-green shader plasma starts playing from time-offset 0)
diff --git a/doc/RFC_XR_Fragments.md b/doc/RFC_XR_Fragments.md index 82b5a52..71f02a1 100644 --- a/doc/RFC_XR_Fragments.md +++ b/doc/RFC_XR_Fragments.md @@ -233,7 +233,7 @@ That way, if the link gets shared, the XR Fragments implementation at `https://m |-------------------|------------|--------------------|----------------------------------------------------------------------| | `#pos` | vector3 | `#pos=0.5,0,0` | positions camera (or XR floor) to xyz-coord 0.5,0,0, | | `#rot` | vector3 | `#rot=0,90,0` | rotates camera to xyz-coord 0.5,0,0 | -| [W3C Media Fragments](https://www.w3.org/TR/media-frags/) | [media fragment](#media%20fragments%20and%20datatypes) | `#t=0,2` `#xywh` | play/loop 3D animation from 0 seconds till 2 seconds| +| [W3C Media Fragments](https://www.w3.org/TR/media-frags/) | [media fragment](#media%20fragments%20and%20datatypes) | `#t=0,2&loop` | play (and loop) 3D animation from 0 seconds till 2 seconds| | | | | but can also crop, animate & configure uv-coordinates/shader uniforms | ## List of metadata for 3D nodes @@ -265,33 +265,28 @@ These are automatic fragment-to-metadata mappings, which only trigger if the 3D > NOTE: below the word 'play' applies to 3D animations embedded in the 3D scene(file) **but also** media defined in `src`-metadata like audio/video-files (mp3/mp4 e.g.) | type | syntax | example | info | -|-------------------------------|------------------------|-----------------|----------------------| -| vector2 | x,y | 2,3.0 | 2-dimensional vector | -| vector3 | x,y,z | 2,3.0,4 | 3-dimensional vector | -| temporal W3C media fragment | t=x | 0 | play from 0 seconds to end (and stop) | -| temporal W3C media fragment | t=x,y | 0,2 | play from 0 seconds till 2 seconds (and stop) | -| temporal W3C media fragment * | t=[l:]x,y | l:0,1 | play [as loop] between `x` and `y` | -| temporal W3C media fragment * | uv=u,v | uv:0,0,1,1 | set uv offset (default `0,0`) | -| temporal W3C media fragment * | s=x | 1 | set playback speed of audio/video/3D anim | -| temporal W3C media fragment * | suv=[l:]uspeed,vspeed | uv:l:0.1,0.2 | set uv scroll speed of (default `1,1` is instant) [`l:` means infinite texturescrolling] otherwise new `u,v` values will be lerped to | +|-------------------------------|-----------------------------------|-----------------|----------------------| +| vector2 | x,y | 2,3.0 | 2-dimensional vector | +| vector3 | x,y,z | 2,3.0,4 | 3-dimensional vector | +| temporal W3C media fragment | t=x | 0 | play from 0 seconds to end (and stop) | +| temporal W3C media fragment | t=x,y | 0,2 | play from 0 seconds till 2 seconds (and stop) | +| temporal W3C media fragment * | s=x | 1 | set playback speed of audio/video/3D anim | +| temporal W3C media fragment * | [-]loop | loop | enable looped playback of audio/video/3D anim | +| | | -loop | disable looped playback (does not affect playbackstate of media) | +| vector2 | uv=uv,v,uspeed,vspeed | 0,0 | set uv offset instantly (default speed = `1,1`) | +| | | +0.5,+0.5 | scroll instantly by adding 0.5 to the current uv coordinates | +| | | 0.2,1,0.1,0.1 | scroll (lerp) to uv coordinate `0,2,1` with `0.1` units per second | +| | | 0,0,0,+0.1 | scroll v coordinates with `0.1` units per second (infinitely) | | media parameter (shader uniform) | u:= | u:color=1,0,0 | set shader uniform value | -> \* = this is extending the [W3C media fragments](https://www.w3.org/TR/media-frags/#mf-advanced) with finergrained playback/viewport-control: +> \* = this is extending the [W3C media fragments](https://www.w3.org/TR/media-frags/#mf-advanced) with (missing) playback/viewport-control. Normally `#t=0,2` implies setting start/stop-values AND starting playback, whereas `#s=0&loop` allows pausing a video, speeding up/slowing down media, as well as enabling/disabling looping. - -| extension | info | -|------------------|---------| -| `l:` specifices loop | `t=0,2` specifies oneshot-play (default) whereas `t=l:0,2` indicates looped-play | -| `#uv=` specifies uv-coordinates | allows offsetting the uv-coordinates | -| `#suv=` specifies scrollspeed of uv-coordinates | allows single/infinite uv-scrolling | -| `#s` specifies playback speed | being able to specify loop(speed) of audio/video | - -> The rationale not to extend the `xywh`-media fragment is that 3D geometries deal with triangular polygons (not rectangular). +> The rationale for `uv` is that the `xywh` Media Fragment deals with rectangular media, which does not translate well to 3D models (which use triangular polygons, not rectangular) positioned by uv-coordinates. Example URI's: -* `https://images.org/credits.jpg#t=0&suv=l:0,0.1` (infinite vertical texturescrolling) -* `https://video.org/organogram.mp4#t=0&suv:0.1,0.1&uv=0.3,0.3` (animated zoom towards region in video) +* `https://images.org/credits.jpg#uv=0,0,0,+0.1` (infinite vertical texturescrolling) +* `https://video.org/organogram.mp4#t=0&loop&uv:0.1,0.1,0.3,0.3` (animated zoom towards region in looped video) * `https://shaders.org/plasma.glsl#t=0&u:col1=1,0,0&u:col2=0,1,0` (red-green shader plasma starts playing from time-offset 0) ``` diff --git a/doc/RFC_XR_Fragments.txt b/doc/RFC_XR_Fragments.txt index 8216629..3c08de3 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 8 February 2024 +Internet-Draft 9 February 2024 Intended status: Informational @@ -43,7 +43,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 11 August 2024. + This Internet-Draft will expire on 12 August 2024. Copyright Notice @@ -53,7 +53,7 @@ Copyright Notice -van Kammen Expires 11 August 2024 [Page 1] +van Kammen Expires 12 August 2024 [Page 1] Internet-Draft XR Fragments February 2024 @@ -109,7 +109,7 @@ Table of Contents -van Kammen Expires 11 August 2024 [Page 2] +van Kammen Expires 12 August 2024 [Page 2] Internet-Draft XR Fragments February 2024 @@ -165,7 +165,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 3] +van Kammen Expires 12 August 2024 [Page 3] Internet-Draft XR Fragments February 2024 @@ -221,7 +221,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 4] +van Kammen Expires 12 August 2024 [Page 4] Internet-Draft XR Fragments February 2024 @@ -277,7 +277,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 5] +van Kammen Expires 12 August 2024 [Page 5] Internet-Draft XR Fragments February 2024 @@ -333,7 +333,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 6] +van Kammen Expires 12 August 2024 [Page 6] Internet-Draft XR Fragments February 2024 @@ -389,7 +389,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 7] +van Kammen Expires 12 August 2024 [Page 7] Internet-Draft XR Fragments February 2024 @@ -445,7 +445,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 8] +van Kammen Expires 12 August 2024 [Page 8] Internet-Draft XR Fragments February 2024 @@ -469,7 +469,7 @@ Internet-Draft XR Fragments February 2024 | | | | |metallic (*=including | | | | | |children) | +----------------+--------------------------------------+-------------+---------------------+-----------------------+ - | |r | |#soldout*=halfopacity|set material of objects| + | | | |#soldout*=halfopacity|set material of objects| | | | | |tagged with product to | | | | | |material with name | | | | | |metallic | @@ -501,7 +501,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 9] +van Kammen Expires 12 August 2024 [Page 9] Internet-Draft XR Fragments February 2024 @@ -514,97 +514,98 @@ Internet-Draft XR Fragments February 2024 | the 3D scene(file) *but also* media defined in src-metadata like | audio/video-files (mp3/mp4 e.g.) - +=========+=======================+============+====================+ - |type | syntax |example | info | - +=========+=======================+============+====================+ - |vector2 | x,y |2,3.0 | 2-dimensional | - | | | | vector | - +---------+-----------------------+------------+--------------------+ - |vector3 | x,y,z |2,3.0,4 | 3-dimensional | - | | | | vector | - +---------+-----------------------+------------+--------------------+ - |temporal | t=x |0 | play from 0 | - |W3C media| | | seconds to end | - |fragment | | | (and stop) | - +---------+-----------------------+------------+--------------------+ - |temporal | t=x,y |0,2 | play from 0 | - |W3C media| | | seconds till 2 | - |fragment | | | seconds (and | - | | | | stop) | - +---------+-----------------------+------------+--------------------+ - |temporal | t=[l:]x,y |l:0,1 | play [as loop] | - |W3C media| | | between x and y | - |fragment | | | | - |* | | | | - +---------+-----------------------+------------+--------------------+ - |temporal | uv=u,v |uv:0,0,1,1 | set uv offset | - |W3C media| | | (default 0,0) | - |fragment | | | | - |* | | | | - +---------+-----------------------+------------+--------------------+ - |temporal | s=x |1 | set playback | - |W3C media| | | speed of audio/ | - |fragment | | | video/3D anim | - |* | | | | - +---------+-----------------------+------------+--------------------+ - |temporal | suv=[l:]uspeed,vspeed |uv:l:0.1,0.2| set uv scroll | - |W3C media| | | speed of (default | - |fragment | | | 1,1 is instant) | - |* | | | [l: means | - | | | | infinite | - | | | | texturescrolling] | - | | | | otherwise new u,v | + +=========+=======================+===============+================+ + |type | syntax | example | info | + +=========+=======================+===============+================+ + |vector2 | x,y | 2,3.0 | 2-dimensional | + | | | | vector | + +---------+-----------------------+---------------+----------------+ + |vector3 | x,y,z | 2,3.0,4 | 3-dimensional | + | | | | vector | + +---------+-----------------------+---------------+----------------+ + |temporal | t=x | 0 | play from 0 | + |W3C media| | | seconds to end | + |fragment | | | (and stop) | + +---------+-----------------------+---------------+----------------+ + |temporal | t=x,y | 0,2 | play from 0 | + |W3C media| | | seconds till 2 | + |fragment | | | seconds (and | + | | | | stop) | + +---------+-----------------------+---------------+----------------+ + |temporal | s=x | 1 | set playback | + |W3C media| | | speed of | + |fragment | | | audio/video/3D | + |* | | | anim | + +---------+-----------------------+---------------+----------------+ + |temporal | [-]loop | loop | enable looped | + |W3C media| | | playback of | + |fragment | | | audio/video/3D | + |* | | | anim | + +---------+-----------------------+---------------+----------------+ + | | | -loop | disable looped | + | | | | playback (does | + | | | | not affect | + | | | | playbackstate | + | | | | of media) | + +---------+-----------------------+---------------+----------------+ + |vector2 | uv=uv,v,uspeed,vspeed | 0,0 | set uv offset | + | | | | instantly | + | | | | (default speed | + | | | | = 1,1) | + +---------+-----------------------+---------------+----------------+ + | | | +0.5,+0.5 | scroll | -van Kammen Expires 11 August 2024 [Page 10] +van Kammen Expires 12 August 2024 [Page 10] Internet-Draft XR Fragments February 2024 - | | | | values will be | - | | | | lerped to | - +---------+-----------------------+------------+--------------------+ - |media | u:==#rot=0,90,0 | vector3 | rotate camera | +--------------------+---------+-----------------------------+ - Table 8 + Table 7 » example implementation (https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/ @@ -725,7 +725,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 13] +van Kammen Expires 12 August 2024 [Page 13] Internet-Draft XR Fragments February 2024 @@ -781,7 +781,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 14] +van Kammen Expires 12 August 2024 [Page 14] Internet-Draft XR Fragments February 2024 @@ -807,7 +807,7 @@ Internet-Draft XR Fragments February 2024 | | |foo.mp3#0,0,0 | +--------+--------+---------------------------------------------------+ - Table 9 + Table 8 Here's an ascii representation of a 3D scene-graph with 3D objects ◻ which embeds remote & local 3D objects ◻ with/out using filters: @@ -837,7 +837,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 15] +van Kammen Expires 12 August 2024 [Page 15] Internet-Draft XR Fragments February 2024 @@ -893,7 +893,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 16] +van Kammen Expires 12 August 2024 [Page 16] Internet-Draft XR Fragments February 2024 @@ -929,7 +929,7 @@ Internet-Draft XR Fragments February 2024 | | | ://somefile.gltf#pos=1,1,0 | +----------+------------------+----------------------------+ - Table 10 + Table 9 1. clicking an outbound ''external''- or ''file URI'' fully replaces the current scene and assumes pos=0,0,0&rot=0,0,0 by default @@ -949,7 +949,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 17] +van Kammen Expires 12 August 2024 [Page 17] Internet-Draft XR Fragments February 2024 @@ -1005,7 +1005,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 18] +van Kammen Expires 12 August 2024 [Page 18] Internet-Draft XR Fragments February 2024 @@ -1061,7 +1061,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 19] +van Kammen Expires 12 August 2024 [Page 19] Internet-Draft XR Fragments February 2024 @@ -1117,7 +1117,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 20] +van Kammen Expires 12 August 2024 [Page 20] Internet-Draft XR Fragments February 2024 @@ -1134,7 +1134,7 @@ Internet-Draft XR Fragments February 2024 | | then only show object with price above 10 | +--------------------+-------------------------------------------+ - Table 11 + Table 10 It's simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling: @@ -1169,11 +1169,11 @@ Internet-Draft XR Fragments February 2024 | | embedded objects (starting with #) | +----------+----------------------------------------------+ - Table 12 + Table 11 -van Kammen Expires 11 August 2024 [Page 21] +van Kammen Expires 12 August 2024 [Page 21] Internet-Draft XR Fragments February 2024 @@ -1229,7 +1229,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 22] +van Kammen Expires 12 August 2024 [Page 22] Internet-Draft XR Fragments February 2024 @@ -1285,7 +1285,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 23] +van Kammen Expires 12 August 2024 [Page 23] Internet-Draft XR Fragments February 2024 @@ -1341,7 +1341,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 24] +van Kammen Expires 12 August 2024 [Page 24] Internet-Draft XR Fragments February 2024 @@ -1390,14 +1390,14 @@ Internet-Draft XR Fragments February 2024 | foo.gltf#house | other objects with tag house or todo | +------------------+--------------------------------------+ - Table 13 + Table 12 -van Kammen Expires 11 August 2024 [Page 25] +van Kammen Expires 12 August 2024 [Page 25] Internet-Draft XR Fragments February 2024 @@ -1453,7 +1453,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 26] +van Kammen Expires 12 August 2024 [Page 26] Internet-Draft XR Fragments February 2024 @@ -1509,7 +1509,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 27] +van Kammen Expires 12 August 2024 [Page 27] Internet-Draft XR Fragments February 2024 @@ -1565,7 +1565,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 28] +van Kammen Expires 12 August 2024 [Page 28] Internet-Draft XR Fragments February 2024 @@ -1621,7 +1621,7 @@ xrtext = { -van Kammen Expires 11 August 2024 [Page 29] +van Kammen Expires 12 August 2024 [Page 29] Internet-Draft XR Fragments February 2024 @@ -1677,7 +1677,7 @@ console.log( xrtext.encode(text,tags) ) // multiplex text & bibtex back to -van Kammen Expires 11 August 2024 [Page 30] +van Kammen Expires 12 August 2024 [Page 30] Internet-Draft XR Fragments February 2024 @@ -1733,7 +1733,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 31] +van Kammen Expires 12 August 2024 [Page 31] Internet-Draft XR Fragments February 2024 @@ -1789,7 +1789,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 32] +van Kammen Expires 12 August 2024 [Page 32] Internet-Draft XR Fragments February 2024 @@ -1845,7 +1845,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 33] +van Kammen Expires 12 August 2024 [Page 33] Internet-Draft XR Fragments February 2024 @@ -1901,7 +1901,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 34] +van Kammen Expires 12 August 2024 [Page 34] Internet-Draft XR Fragments February 2024 @@ -1957,7 +1957,7 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 35] +van Kammen Expires 12 August 2024 [Page 35] Internet-Draft XR Fragments February 2024 @@ -2013,12 +2013,12 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 36] +van Kammen Expires 12 August 2024 [Page 36] Internet-Draft XR Fragments February 2024 - Table 14 + Table 13 @@ -2069,4 +2069,4 @@ Internet-Draft XR Fragments February 2024 -van Kammen Expires 11 August 2024 [Page 37] +van Kammen Expires 12 August 2024 [Page 37] diff --git a/doc/RFC_XR_Fragments.xml b/doc/RFC_XR_Fragments.xml index f325cb9..e65b895 100644 --- a/doc/RFC_XR_Fragments.xml +++ b/doc/RFC_XR_Fragments.xml @@ -345,7 +345,7 @@ That way, if the link gets shared, the XR Fragments implementation at https: -r + #soldout*=halfopacity set material of objects tagged with product to material with name metallic @@ -402,20 +402,6 @@ That way, if the link gets shared, the XR Fragments implementation at https: play from 0 seconds till 2 seconds (and stop) - -temporal W3C media fragment * -t=[l:]x,y -l:0,1 -play [as loop] between x and y - - - -temporal W3C media fragment * -uv=u,v -uv:0,0,1,1 -set uv offset (default 0,0) - - temporal W3C media fragment * s=x @@ -425,9 +411,44 @@ That way, if the link gets shared, the XR Fragments implementation at https: temporal W3C media fragment * -suv=[l:]uspeed,vspeed -uv:l:0.1,0.2 -set uv scroll speed of (default 1,1 is instant) [l: means infinite texturescrolling] otherwise new u,v values will be lerped to +[-]loop +loop +enable looped playback of audio/video/3D anim + + + + + +-loop +disable looped playback (does not affect playbackstate of media) + + + +vector2 +uv=uv,v,uspeed,vspeed +0,0 +set uv offset instantly (default speed = 1,1) + + + + + ++0.5,+0.5 +scroll instantly by adding 0.5 to the current uv coordinates + + + + + +0.2,1,0.1,0.1 +scroll (lerp) to uv coordinate 0,2,1 with 0.1 units per second + + + + + +0,0,0,+0.1 +scroll v coordinates with 0.1 units per second (infinitely) @@ -437,42 +458,13 @@ That way, if the link gets shared, the XR Fragments implementation at https: vec2 -
* = this is extending the W3C media fragments with finergrained playback/viewport-control: -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
extensioninfo
l: specifices loopt=0,2 specifies oneshot-play (default) whereas t=l:0,2 indicates looped-play
#uv= specifies uv-coordinatesallows offsetting the uv-coordinates
#suv= specifies scrollspeed of uv-coordinatesallows single/infinite uv-scrolling
#s specifies playback speedbeing able to specify loop(speed) of audio/video
The rationale not to extend the xywh-media fragment is that 3D geometries deal with triangular polygons (not rectangular). +
* = this is extending the W3C media fragments with (missing) playback/viewport-control. Normally #t=0,2 implies setting start/stop-values AND starting playback, whereas #s=0&loop allows pausing a video, speeding up/slowing down media, as well as enabling/disabling looping. +The rationale for uv is that the xywh Media Fragment deals with rectangular media, which does not translate well to 3D models (which use triangular polygons, not rectangular) positioned by uv-coordinates.
Example URI's:
    -
  • https://images.org/credits.jpg#t=0&suv=l:0,0.1 (infinite vertical texturescrolling)
  • -
  • https://video.org/organogram.mp4#t=0&suv:0.1,0.1&uv=0.3,0.3 (animated zoom towards region in video)
  • +
  • https://images.org/credits.jpg#uv=0,0,0,+0.1 (infinite vertical texturescrolling)
  • +
  • https://video.org/organogram.mp4#t=0&loop&uv:0.1,0.1,0.3,0.3 (animated zoom towards region in looped video)
  • https://shaders.org/plasma.glsl#t=0&u:col1=1,0,0&u:col2=0,1,0 (red-green shader plasma starts playing from time-offset 0)
diff --git a/example/aframe/sandbox/index.html b/example/aframe/sandbox/index.html index 4ec309b..d29c367 100644 --- a/example/aframe/sandbox/index.html +++ b/example/aframe/sandbox/index.html @@ -11,7 +11,7 @@ - + diff --git a/src/3rd/js/XRWG.js b/src/3rd/js/XRWG.js index 425c9b9..2973434 100644 --- a/src/3rd/js/XRWG.js +++ b/src/3rd/js/XRWG.js @@ -20,7 +20,7 @@ XRWG.match = (str,types,level) => { return n }) str = str.toLowerCase() - .replace(/[-\*]/,'') // remove excludes and wildcards + .replace(/[!-\*]/g,'') // remove excludes and wildcards if( level <10 ) res = res.filter( (n) => n.key == str ) if( level >=10 ) res = res.filter( (n) => n.word == str || n.key == str ) if( level >30 ) res = res.filter( (n) => n.word.match(str) || n.key == str ) @@ -39,11 +39,12 @@ XRWG.generate = (opts) => { if( !key || key.match(/(^#$|name)/) ) return let node = XRWG.get( XRWG.cleankey(key) ) if( node ){ + node.types.push(type) node.nodes.push(spatialNode) }else{ - node = { word: XRWG.cleankey(key), key, nodes:[spatialNode] } + node = { word: XRWG.cleankey(key), key, nodes:[spatialNode], types:[] } if( spatialNode.userData[key] ) node.value = spatialNode.userData[key] - node[type] = true + node.types.push(type) xrf.emit('XRWGnode',node) XRWG.push( node ) } diff --git a/src/3rd/js/three/xrf/dynamic/material.js b/src/3rd/js/three/xrf/dynamic/material.js index 9561f66..59cd371 100644 --- a/src/3rd/js/three/xrf/dynamic/material.js +++ b/src/3rd/js/three/xrf/dynamic/material.js @@ -17,10 +17,14 @@ xrf.frag.dynamic.material = function(v,opts){ xrf.frag.dynamic.material.setMatch = function(match,material,v){ match.map( (m) => { - m.nodes.map( (n) => { - n.material = setMaterial( n, material, v.reset ) - if( v.filter.q.deep ) n.traverse( (c) => c.material && setMaterial( c, material, v.reset ) ) - }) + for( let i in m.types ){ + let type = m.types[i] + let node = m.nodes[i] + if (type == 'name' || type == 'tag'){ + setMaterial( node, material, v.reset ) + if( v.filter.q.deep ) node.traverse( (c) => c.material && setMaterial( c, material, v.reset ) ) + } + } }) } diff --git a/src/3rd/js/three/xrf/loop.js b/src/3rd/js/three/xrf/loop.js new file mode 100644 index 0000000..36093ce --- /dev/null +++ b/src/3rd/js/three/xrf/loop.js @@ -0,0 +1,17 @@ +xrf.frag.loop = function(v, opts){ + let { frag, mesh, model, camera, scene, renderer, THREE} = opts + + // handle object media players + if( mesh && mesh.media ){ + for( let i in mesh.media ) mesh.media[i].pub(v) + return + } + + // otherwise handle global 3D animations + xrf.mixers.map ( (mixer) => { + // update loop + mixer.loop.enabled = v.loop + + }) + +} diff --git a/src/3rd/js/three/xrf/s.js b/src/3rd/js/three/xrf/s.js index b1a6dc8..6f14a1f 100644 --- a/src/3rd/js/three/xrf/s.js +++ b/src/3rd/js/three/xrf/s.js @@ -1,11 +1,21 @@ -xrf.frag.suv = function(v, opts){ +xrf.frag.s = function(v, opts){ let { frag, mesh, model, camera, scene, renderer, THREE} = opts - if( !mesh.geometry ) return // nothing to do here + // handle object media players + if( mesh && mesh.media ){ + for( let i in mesh.media ) mesh.media[i].pub(v) + return + } + + // otherwise handle global 3D animations + xrf.mixers.map ( (mixer) => { + mixer.s = v + + // update speed + mixer.timeScale = v.x || 1.0 + mixer.loop.speed = v.x || 1.0 + mixer.loop.speedAbs = Math.abs( v.x ) + + }) - xrf.frag.uv.init(mesh) - mesh.suv.x = v.x - mesh.suv.y = v.y !== undefined ? v.y : v.x - mesh.suv.loop = v.loop === true ? true : false - xrf.frag.uv.scroll(mesh) } diff --git a/src/3rd/js/three/xrf/src/audio.js b/src/3rd/js/three/xrf/src/audio.js index 9c21c76..7c61f0c 100644 --- a/src/3rd/js/three/xrf/src/audio.js +++ b/src/3rd/js/three/xrf/src/audio.js @@ -39,32 +39,34 @@ let loadAudio = (mimetype) => function(url,opts){ mesh.add(sound) - sound.pub = (t) => { + sound.set = (mediafragment,v) => { try{ - sound.t = t - if( sound.isPlaying && t.y != undefined ) sound.stop() - if( sound.isPlaying && t.y == undefined ) sound.pause() + sound[mediafragment] = v + + if( mediafragment == 't'){ + sound.pause() + if( sound.isPlaying && v.y != undefined && v.x == v.y ) return - let hardcodedLoop = frag.t != undefined - t = hardcodedLoop ? { ...frag.t, x: t.x} : t // override with hardcoded metadata except playstate (x) - if( t && t.x != 0 ){ - // *TODO* https://stackoverflow.com/questions/12484052/how-can-i-reverse-playback-in-web-audio-api-but-keep-a-forward-version-as-well - t.x = Math.abs(t.x) - sound.setPlaybackRate( t.x ) // WebAudio does not support negative playback - // setting loop - if( t.z ) sound.setLoop( true ) // apply embedded audio/video samplerate/fps or global mixer fps - let loopStart = hardcodedLoop ? t.y : t.y * buffer.sampleRate; - let loopEnd = hardcodedLoop ? t.z : t.z * buffer.sampleRate; - let timeStart = loopStart > 0 ? loopStart : (t.y == undefined ? xrf.model.mixer.time : t.y) - - if( t.z > 0 ) sound.setLoopEnd( loopEnd ) - if( t.y != undefined ){ - sound.setLoopStart( loopStart ) - sound.offset = loopStart - } + sound.setLoopStart(v.x * buffer.sampleRate ); + sound.setLoopEnd(v.y * buffer.sampleRate ); + sound.offset = v.x * buffer.sampleRate ; sound.play() } + + if( mediafragment == 's'){ + // *TODO* https://stackoverflow.com/questions/12484052/how-can-i-reverse-playback-in-web-audio-api-but-keep-a-forward-version-as-well + sound.pause() + sound.setPlaybackRate( Math.abs(v.x) ) // WebAudio does not support negative playback + sound.play() + } + + if( mediafragment == 'loop'){ + sound.pause() + sound.setLoop( v.loop ) + sound.play() + } + debugger }catch(e){ console.warn(e) } } diff --git a/src/3rd/js/three/xrf/src/video.js b/src/3rd/js/three/xrf/src/video.js index 88fb52f..8075a4f 100644 --- a/src/3rd/js/three/xrf/src/video.js +++ b/src/3rd/js/three/xrf/src/video.js @@ -28,15 +28,20 @@ let loadVideo = (mimetype) => function(url,opts){ video.src = url video.speed = 1.0 video.looping = false - video.pub = (t) => { - video.t = t - video.pause() - if( t.x !== undefined && t.x == t.y ) return // stop paused - else{ - video.currentTime = t.x - video.time = t.x + video.set = (mediafragment,v) => { + video[mediafragment] = v + + if( mediafragment == 't'){ + video.pause() + if( t.x !== undefined && t.x == t.y ) return // stop paused + else{ + video.currentTime = t.x + video.time = t.x + video.play() + } + } + if( mediafragment == 's' ){ video.playbackRate = Math.abs( video.speed ) // html5 video does not support reverseplay :/ - video.play() } } } diff --git a/src/3rd/js/three/xrf/suv.js b/src/3rd/js/three/xrf/suv.js deleted file mode 100644 index b974bbc..0000000 --- a/src/3rd/js/three/xrf/suv.js +++ /dev/null @@ -1,11 +0,0 @@ -xrf.frag.suv = function(v, opts){ - let { frag, mesh, model, camera, scene, renderer, THREE} = opts - - if( !mesh.geometry ) return // nothing to do here - - xrf.frag.uv.init(mesh) - mesh.suv.x = v.x - mesh.suv.y = v.y !== undefined ? v.y : v.x - mesh.suv.loop = v.loop === true ? true : false - mesh.onBeforeRender = xrf.frag.uv.scroll -} diff --git a/src/3rd/js/three/xrf/t.js b/src/3rd/js/three/xrf/t.js index 670c0e3..cfb467f 100644 --- a/src/3rd/js/three/xrf/t.js +++ b/src/3rd/js/three/xrf/t.js @@ -5,7 +5,7 @@ xrf.frag.t = function(v, opts){ // handle object media players if( mesh && mesh.media ){ - for( let i in mesh.media ) mesh.media[i].pub(v) + for( let i in mesh.media ) mesh.media[i].set("t",v) return } @@ -21,7 +21,7 @@ xrf.frag.t = function(v, opts){ mixer.t = v // update speed - mixer.timeScale = mixer.loop.speed || 1.0 + mixer.timeScale = mixer.loop.speed mixer.loop.speedAbs = Math.abs( mixer.timeScale ) mixer.updateLoop( v ) @@ -81,15 +81,17 @@ xrf.addEventListener('parseModel', (opts) => { } mixer.updateLoop = (t) => { - mixer.loop.timeStart = t.x != undefined ? t.x : mixer.loop.timeStart - mixer.loop.timeStop = t.y != undefined ? t.y : mixer.loop.timeStop + if( t ){ + mixer.loop.timeStart = t.x != undefined ? t.x : mixer.loop.timeStart + mixer.loop.timeStop = t.y != undefined ? t.y : mixer.duration + } mixer.actions.map( (action) => { if( mixer.loop.timeStart != undefined ){ action.time = mixer.loop.timeStart action.setLoop( xrf.THREE.LoopOnce, ) action.timeScale = mixer.timeScale action.enabled = true - if( t.x === 0 ) action.play() + if( t && t.x === 0 ) action.play() } }) mixer.setTime(mixer.loop.timeStart) @@ -98,16 +100,19 @@ xrf.addEventListener('parseModel', (opts) => { mixer.checkZombies( model.animations) } - // update loop when needed + // monkeypatch: update loop when needed if( !mixer.update.patched ){ + let update = mixer.update mixer.update = function(time){ mixer.time = Math.abs(mixer.time) if( time == 0 ) return update.call(this,time) // loop jump - if( mixer.loop.speed > 0.0 && (mixer.loop.timeStop > 0 && mixer.time > mixer.loop.timeStop) ){ - setTimeout( (time,anims) => mixer.updateLoop(time), 0, mixer.loop.timeStart ) // prevent recursion + if( mixer.loop.timeStop > 0 && mixer.time > mixer.loop.timeStop ){ + if( mixer.loop.enabled ){ + setTimeout( () => mixer.updateLoop(), 0 ) // prevent recursion + }else mixer.stop() } return update.call( this, time ) } @@ -142,7 +147,7 @@ xrf.addEventListener('render', (opts) => { xrf.mixers.map( (m) => m.isPlaying && (m.update( time )) ) // update active camera in case selected by dynamicKey in URI - if( xrf.model.camera && model.mixer.isPlaying ){ + if( xrf.model.camera && xrf.model.camera.length && model.mixer.isPlaying ){ let cam = xrf.camera.getCam() // cam.fov = model.cameras[0].fov (why is blender not exporting radians?) diff --git a/src/3rd/js/three/xrf/uv.js b/src/3rd/js/three/xrf/uv.js index 34bbcc5..380362a 100644 --- a/src/3rd/js/three/xrf/uv.js +++ b/src/3rd/js/three/xrf/uv.js @@ -2,55 +2,69 @@ xrf.frag.uv = function(v, opts){ let { frag, mesh, model, camera, scene, renderer, THREE} = opts if( !mesh.geometry ) return // nothing to do here + if( v.floats.length != 4 ) return xrf.frag.uv.init(mesh) - mesh.uv.x = v.x - mesh.uv.y = v.y !== undefined ? v.y : v.x + mesh.uv.u = v.floats[0] + mesh.uv.v = v.floats[1] + mesh.uv.uspeed = v.floats[2] + mesh.uv.vspeed = v.floats[3] + mesh.uv.uloop = v.shift[2] + mesh.uv.vloop = v.shift[3] + mesh.onBeforeRender = xrf.frag.uv.scroll } xrf.frag.uv.init = function(mesh){ - if( !mesh.uv ) mesh.uv = {x:0, y:0, w:1, h:1, uv:false} - if( !mesh.suv ) mesh.suv = {x:1, y:1, loop:false } + if( !mesh.uv ) mesh.uv = {u:0, v:0, uspeed:1, vspeed:1, uloop:false, vloop:false, uv:false} + let uv = mesh.geometry.getAttribute("uv") - if( !uv.old ) uv.old = uv.clone() + if( !uv.old ) uv.old = mesh.geometry.getAttribute("uv").clone() } xrf.frag.uv.scroll = function(){ - if( this.suv.x > 0.0 || this.suv.y > 0.0 ){ + if( this.uv.uspeed > 0.0 || this.uv.vspeed > 0.0 ){ - let diff = 0.0 // distance to end-state (non-looping mode) + let diffU = 0.0 // distance to end-state (non-looping mode) + let diffV = 0.0 // distance to end-state (non-looping mode) let uv = this.geometry.getAttribute("uv") // translate! for( let i = 0; i < uv.count; i++ ){ let u = uv.getX(i) let v = uv.getY(i) - let uTarget = uv.old.getX(i) + this.uv.x - let vTarget = uv.old.getY(i) + this.uv.y + let uTarget = uv.old.getX(i) + this.uv.u + let vTarget = uv.old.getY(i) + this.uv.v - if( this.suv.loop ){ - u += this.suv.x * xrf.clock.delta - v += this.suv.y * xrf.clock.delta + // scroll U + if( this.uv.uloop ){ + u += this.uv.uspeed * xrf.clock.delta }else{ - // recover from super-high uv-values due to looped scrolling if( Math.abs(u-uTarget) > 1.0 ) u = uv.old.getX(i) - if( Math.abs(v-vTarget) > 1.0 ) v = uv.old.getY(i) + u = u > uTarget ? u + (this.uv.uspeed * -xrf.clock.delta) + : u + (this.uv.uspeed * xrf.clock.delta) + diffU += Math.abs( u - uTarget ) // are we done yet? (non-looping mode) + } - u = u > uTarget ? u + (this.suv.x * -xrf.clock.delta) - : u + (this.suv.x * xrf.clock.delta) - v = v > vTarget ? v + (this.suv.y * -xrf.clock.delta) - : v + (this.suv.y * xrf.clock.delta) - diff += Math.abs( u - uTarget ) // are we done yet? (non-looping mode) - diff += Math.abs( v - vTarget ) + // scroll V + if( this.uv.vloop ){ + v += this.uv.vspeed * xrf.clock.delta + }else{ + // recover from super-high uv-values due to looped scrolling + if( Math.abs(v-vTarget) > 1.0 ) v = uv.old.getY(i) + v = v > vTarget ? v + (this.uv.vspeed * -xrf.clock.delta) + : v + (this.uv.vspeed * xrf.clock.delta) + diffV += Math.abs( v - vTarget ) } uv.setXY(i,u,v) } uv.needsUpdate = true - if( !this.suv.loop && diff < 0.05 ){ // stop animating if done + if( (!this.uv.uloop && diffU < 0.05) && + (!this.uv.vloop && diffV < 0.05) + ){ // stop animating if done this.onBeforeRender = function(){} } } diff --git a/src/3rd/js/three/xrf/xywh.js b/src/3rd/js/three/xrf/xywh.js deleted file mode 100644 index 8eb6635..0000000 --- a/src/3rd/js/three/xrf/xywh.js +++ /dev/null @@ -1,9 +0,0 @@ -xrf.frag.xywh = function(v, opts){ - let { frag, mesh, model, camera, scene, renderer, THREE} = opts - xrf.mediafragment.init(mesh) - mesh.xywh.x = v.floats[0] - mesh.xywh.y = v.floats[1] !== undefined ? v.floats[1] : mesh.xywh.x - mesh.xywh.w = v.floats[2] !== undefined ? v.floats[2] : mesh.xywh.y - mesh.xywh.h = v.floats[3] !== undefined ? v.floats[3] : mesh.xywh.w - // TODO: nondestructive cropping of texture (not superimportant now) -} diff --git a/src/Test.hx b/src/Test.hx index 517466d..6f733e3 100644 --- a/src/Test.hx +++ b/src/Test.hx @@ -18,10 +18,8 @@ class Test { test( "url.json", Spec.load("src/spec/url.json") ); test( "pos.json", Spec.load("src/spec/pos.json") ); test( "t.json", Spec.load("src/spec/t.json") ); - test( "xywh.json", Spec.load("src/spec/xywh.json") ); test( "s.json", Spec.load("src/spec/s.json") ); - test( "suv.json", Spec.load("src/spec/suv.json") ); - test( "suv.json", Spec.load("src/spec/uv.json") ); + test( "uv.json", Spec.load("src/spec/uv.json") ); test( "filter.selectors.json", Spec.load("src/spec/filter.selectors.json") ); //test( Spec.load("src/spec/tmp.json") ); if( errors > 1 ) trace("\n-----\n[ ❌] "+errors+" errors :/"); @@ -51,9 +49,8 @@ class Test { if( item.expect.fn == "equal.xy" ) valid = equalXY(res,item); if( item.expect.fn == "equal.xyz" ) valid = equalXYZ(res,item); if( item.expect.fn == "equal.mediafragmentT" ) valid = equalMediaFragment(res,item,"t"); - if( item.expect.fn == "equal.mediafragmentXYWH") valid = equalMediaFragment(res,item,"xywh"); + if( item.expect.fn == "equal.mediafragmentUV") valid = equalMediaFragment(res,item,"uv"); if( item.expect.fn == "equal.mediafragmentS") valid = equalMediaFragment(res,item,"s"); - if( item.expect.fn == "equal.mediafragmentSUV") valid = equalMediaFragment(res,item,"suv"); if( item.expect.fn == "testFilterRoot" ) valid = res.exists(item.expect.input[0]) && res.get(item.expect.input[0]).filter.get().root == item.expect.out; if( item.expect.fn == "testFilterDeep" ) valid = res.exists(item.expect.input[0]) && res.get(item.expect.input[0]).filter.get().deep == item.expect.out; var ok:String = valid ? "[ ✔ ] " : "[ ❌] "; diff --git a/src/spec/suv.json b/src/spec/suv.json deleted file mode 100644 index b99b461..0000000 --- a/src/spec/suv.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"fn":"url","data":"http://foo.com?foo=1#suv=l:0,0.1", "expect":{ "fn":"equal.mediafragmentSUV", "input":"1","out":"0.1"},"label":"suv"}, - {"fn":"url","data":"http://foo.com?foo=1#suv=0.2,0.1", "expect":{ "fn":"equal.mediafragmentSUV", "input":"0","out":"0.2"},"label":"suv looped"} -] diff --git a/src/spec/uv.json b/src/spec/uv.json index 838f5c0..6ada382 100644 --- a/src/spec/uv.json +++ b/src/spec/uv.json @@ -1,3 +1,4 @@ [ - {"fn":"url","data":"http://foo.com?foo=1#uv=1.2,2.2", "expect":{ "fn":"equal.xy", "input":"uv","out":"1.2,2.2"},"label":"equal.string uv"} + {"fn":"url","data":"http://foo.com?foo=1#uv=1.2,2.2", "expect":{ "fn":"equal.xy", "input":"uv","out":"1.2,2.2"},"label":"equal.string uv"}, + {"fn":"url","data":"http://foo.com?foo=1#uv=1.2,2.2,+1,+1", "expect":{ "fn":"equal.xyz", "input":"uv","out":"1.2,2.2,1"},"label":"equal.string uv"} ] diff --git a/src/spec/xywh.json b/src/spec/xywh.json deleted file mode 100644 index 203a3b5..0000000 --- a/src/spec/xywh.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"fn":"url","data":"http://foo.com?foo=1#xywh=0,0,1,1", "expect":{ "fn":"equal.mediafragmentXYWH", "input":"2","out":"1"},"label":"xywh"}, - {"fn":"url","data":"http://foo.com?foo=1#xywh=1,100,400,500", "expect":{ "fn":"equal.mediafragmentXYWH", "input":"3","out":"500"},"label":"a equal.mediafragment"} -] diff --git a/src/xrfragment/Parser.hx b/src/xrfragment/Parser.hx index 482b098..fbbe99d 100644 --- a/src/xrfragment/Parser.hx +++ b/src/xrfragment/Parser.hx @@ -25,11 +25,10 @@ class Parser { Frag.set("rot", XRF.QUERY_OPERATOR | XRF.PV_OVERRIDE | XRF.T_VECTOR3 | XRF.METADATA | XRF.NAVIGATOR ); // category: media fragments - Frag.set("t", XRF.PV_OVERRIDE | XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA); - Frag.set("xywh", XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.T_MEDIAFRAG | XRF.NAVIGATOR | XRF.METADATA); - Frag.set("s", XRF.PV_OVERRIDE | XRF.T_MEDIAFRAG | XRF.T_FLOAT ); + Frag.set("t", XRF.PV_OVERRIDE | XRF.T_FLOAT | XRF.T_VECTOR2 | XRF.NAVIGATOR | XRF.METADATA); + Frag.set("s", XRF.PV_OVERRIDE | XRF.T_MEDIAFRAG ); + Frag.set("loop", XRF.PV_OVERRIDE ); Frag.set("uv", XRF.T_VECTOR2 | XRF.T_MEDIAFRAG ); - Frag.set("suv", XRF.T_VECTOR2 | XRF.T_MEDIAFRAG ); // category: author / metadata Frag.set("namespace", XRF.IMMUTABLE | XRF.T_STRING ); diff --git a/src/xrfragment/URI.hx b/src/xrfragment/URI.hx index f763925..6bab93b 100644 --- a/src/xrfragment/URI.hx +++ b/src/xrfragment/URI.hx @@ -38,7 +38,8 @@ class URI { var key:String = splitByEqual[0]; var value:String = ""; if (splitByEqual.length > 1) { - value = StringTools.urlDecode(regexPlus.split(splitByEqual[1]).join(" ")); + if( XRF.isVector.match(splitByEqual[1]) ) value = splitByEqual[1]; + else value = StringTools.urlDecode(regexPlus.split(splitByEqual[1]).join(" ")); } var ok:Bool = Parser.parse(key,value,store,i); // 1. for every recognized fragment key/value-pair call [Parser.parse](#%E2%86%AA%20Parser.parse%28k%2Cv%2Cstore%29) } diff --git a/src/xrfragment/XRF.hx b/src/xrfragment/XRF.hx index cd76e8b..eb425df 100644 --- a/src/xrfragment/XRF.hx +++ b/src/xrfragment/XRF.hx @@ -49,8 +49,9 @@ class XRF { public static var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` ) public static var isDeep:EReg = ~/\*/; // 1. detect deep selectors like `foo*` (reference regex= `/\*$/` ) public static var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` ) - public static var isMediaFrag:EReg = ~/^(l:)?([0-9\.,\*]+)$/; // 1. detect (extended) media fragment + public static var isMediaFrag:EReg = ~/^([0-9\.,\*+-]+)$/; // 1. detect (extended) media fragment public static var isReset:EReg = ~/^!/; // 1. detect reset operation + public static var isShift:EReg = ~/[+-]/; // value holder(s) // |------|------|--------|----------------------------------| public var fragment:String; @@ -59,6 +60,7 @@ class XRF { public var x:Float; // |vector| x,y,z| comma-separated | #pos=1,2,3 | public var y:Float; public var z:Float; + public var shift:Array = new Array(); public var floats:Array = new Array(); public var color:String; // |string| color| FFFFFF (hex) | #fog=5m,FFAACC | public var string:String; // |string| | | #q=-sun | @@ -100,18 +102,18 @@ class XRF { public function guessType(v:XRF, str:String):Void { v.string = str; if( isReset.match(v.fragment) ) v.reset = true; + if( v.fragment == 'loop' ) v.loop = true; if( !Std.isOfType(str,String) ) return; + if( str.length > 0 ){ - if( str.split("l:").length > 1 ){ - str = str.split("l:")[1]; - v.loop = true; - } + if( str.split(",").length > 1){ // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]] var xyzn:Array = str.split(","); // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values if( xyzn.length > 0 ) v.x = Std.parseFloat(xyzn[0]); // 1. anything else will be treated as string-value if( xyzn.length > 1 ) v.y = Std.parseFloat(xyzn[1]); // 1. incompatible value-types will be dropped / not used if( xyzn.length > 2 ) v.z = Std.parseFloat(xyzn[2]); // for( i in 0...xyzn.length ){ + v.shift.push( isShift.match(xyzn[i]) ); v.floats.push( Std.parseFloat(xyzn[i]) ); } } // > the xrfragment specification should stay simple enough @@ -126,6 +128,7 @@ class XRF { v.x = cast(v.int); v.floats.push( cast(v.x) ); } + v.filter = new Filter(v.fragment+"="+v.string); }else v.filter = new Filter(v.fragment); }