This commit is contained in:
Leon van Kammen 2023-11-22 21:03:41 +01:00
parent 4426d6a40e
commit ca0ded3095
10 changed files with 41 additions and 32 deletions

View file

@ -290,7 +290,7 @@ For example, to render a portal with a preview-version of the scene, create an 3
1. set the position of the camera accordingly to the vector3 values of `#pos` 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. `rot` sets the rotation of the camera (only for non-VR/AR headsets)
1. `t` sets the playbackspeed and animation-range of the current scene animation(s) or `src`-mediacontent (video/audioframes e.g., use `t=0,7,7` to 'STOP' at frame 7 e.g.) 1. `t` sets the playbackspeed and animation-range of the current scene animation(s) or `src`-mediacontent (video/audioframes e.g., use `t=0,7,7` to 'STOP' at frame 7 e.g.)
1. in case an `href` does not mention any `pos`-coordinate, `pos=0,0,0` will be assumed 1. after scene load: in case an `href` does not mention any `pos`-coordinate, `pos=0,0,0` will be assumed
Here's an ascii representation of a 3D scene-graph which contains 3D objects `◻` and their metadata: Here's an ascii representation of a 3D scene-graph which contains 3D objects `◻` and their metadata:
@ -374,11 +374,12 @@ Resizing will be happen accordingly to its placeholder object `aquariumcube`, se
7. <b>external</b> `src` values should respect the fallback link mechanism (see [broken links](#broken-links) 7. <b>external</b> `src` values should respect the fallback link mechanism (see [broken links](#broken-links)
8. when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer. 8. when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer.
9. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored. 9. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored.
10. clicking on external `src`-values always allow sourceportation: teleporting to the origin URI to which the object belongs. 10. an external `src`-value should always allow a sourceportation icon within 3 meter: teleporting to the origin URI to which the object belongs.
11. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0` 11. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0`
12. equirectangular detection: when the width of an image is twice the height (aspect 2:1), an equirectangular projection is assumed. 12. when the enduser clicks an href with `#t=1,0,0` (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.)
13. when the enduser clicks an href with `#t=1,0,0` (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.) 13. a non-euclidian portal can be rendered for flat 3D objects (using stencil buffer e.g.) in case ofspatial `src`-values (an object `#world3` or URL `world3.fbx` e.g.).
* `model/gltf-binary`
* `model/gltf+json` * `model/gltf+json`
* `image/png` * `image/png`
* `image/jpg` * `image/jpg`
@ -388,7 +389,7 @@ Resizing will be happen accordingly to its placeholder object `aquariumcube`, se
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192)<br> [» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192)<br>
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/4)<br> [» discussion](https://github.com/coderofsalvation/xrfragment/issues/4)<br>
# Navigating content (internal/outbound href portals) # Navigating content href portals
navigation, portals & mutations navigation, portals & mutations
@ -404,11 +405,11 @@ navigation, portals & mutations
4. URL navigation should always be reflected in the client (in case of javascript: see [[here](https://github.com/coderofsalvation/xrfragment/blob/dev/src/3rd/js/three/navigator.js) for an example navigator). 4. URL navigation should always be reflected in the client (in case of javascript: see [[here](https://github.com/coderofsalvation/xrfragment/blob/dev/src/3rd/js/three/navigator.js) for an example navigator).
5. In XR mode, the navigator back/forward-buttons should be always visible (using a wearable e.g., see [[here](https://github.com/coderofsalvation/xrfragment/blob/dev/example/aframe/sandbox/index.html#L26-L29) for an example wearable) 7. In XR mode, the navigator back/forward-buttons should be always visible (using a wearable e.g., see [[here](https://github.com/coderofsalvation/xrfragment/blob/dev/example/aframe/sandbox/index.html#L26-L29) for an example wearable)
6. in case of navigating to a new [[pos)ition, ''first'' navigate to the ''current position'' so that the ''back-button'' of the ''browser-history'' always refers to the previous position (see [[here](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js#L97)) 8. in case of navigating to a new [[pos)ition, ''first'' navigate to the ''current position'' so that the ''back-button'' of the ''browser-history'' always refers to the previous position (see [[here](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js#L97))
7. portal-rendering: a 2:1 ratio texture-material indicates an equirectangular projection 9. ignore previous rule in special cases, like clicking an `href` using camera-portal collision (the back-button would cause a teleport-loop)
[» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js)<br> [» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/href.js)<br>
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192)<br> [» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/href.gltf#L192)<br>
@ -498,7 +499,7 @@ It's simple but powerful syntax which allows filtering the scene using searcheng
1. filters are a way to traverse a scene, and filter objects based on their name, tag- or property-values. 1. filters are a way to traverse a scene, and filter objects based on their name, tag- or property-values.
* see [an (outdated) example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4) which used a dedicated `q=` variable (now deprecated) * see [an (outdated) example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4) which used a dedicated `q=` variable (now deprecated and usable directly)
## including/excluding ## including/excluding
@ -509,7 +510,7 @@ It's simple but powerful syntax which allows filtering the scene using searcheng
| `=>` `=<`| compare float or int number (`#price=>4` e.g.) | | `=>` `=<`| compare float or int number (`#price=>4` e.g.) |
| `/` | reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) | | `/` | reference to root-scene.<br>Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) |
> \* = `#q=-/cube` hides object `cube` only in the root-scene (not nested `cube` objects)<br> `#q=-cube` hides both object `cube` in the root-scene <b>AND</b> nested `skybox` objects | > \* = `#-/cube` hides object `cube` only in the root-scene (not nested `cube` objects)<br> `#-cube` hides both object `cube` in the root-scene <b>AND</b> nested `skybox` objects |
[» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js) [» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js)
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/filter.gltf#L192) [» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/filter.gltf#L192)
@ -861,12 +862,12 @@ For example:
│ │ │ │
│ index.gltf │ │ index.gltf │
│ │ │ │ │ │
│ │ #: #q=-offlinetext │ │ │ #: #-offlinetext │
│ │ │ │ │ │
│ ├── ◻ buttonA │ │ ├── ◻ buttonA │
│ │ └ href: http://foo.io/campagne.fbx │ │ │ └ href: http://foo.io/campagne.fbx │
│ │ └ href@404: ipfs://foo.io/campagne.fbx │ │ │ └ href@404: ipfs://foo.io/campagne.fbx │
│ │ └ href@400: #q=clienterrortext │ │ │ └ href@400: #clienterrortext
│ │ └ ◻ offlinetext │ │ │ └ ◻ offlinetext │
│ │ │ │ │ │
│ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not) │ └── ◻ embeddedObject <--------- the meshdata inside embeddedObject will (not)
@ -890,9 +891,9 @@ Consider 3D scenes linking to eachother using these `href` values:
These links would all show visible links to math-tagged objects in the scene.<br> These links would all show visible links to math-tagged objects in the scene.<br>
To filter out non-related objects one could take it a step further using filters: To filter out non-related objects one could take it a step further using filters:
* `href: schoolA.edu/projects.gltf#math&q=-topics math` * `href: schoolA.edu/projects.gltf#math&-topics math`
* `href: schoolB.edu/projects.gltf#math&q=-courses math` * `href: schoolB.edu/projects.gltf#math&-courses math`
* `href: university.edu/projects.gltf#math&q=-theme math` * `href: university.edu/projects.gltf#math&-theme math`
> This would hide all object tagged with `topic`, `courses` or `theme` (including math) so that later only objects tagged with `math` will be visible > This would hide all object tagged with `topic`, `courses` or `theme` (including math) so that later only objects tagged with `math` will be visible
@ -944,7 +945,7 @@ This document has no IANA actions.
|placeholder object | a 3D object which with src-metadata (which will be replaced by the src-data.) | |placeholder object | a 3D object which with src-metadata (which will be replaced by the src-data.) |
|src | (HTML-piggybacked) metadata of a 3D object which instances content | |src | (HTML-piggybacked) metadata of a 3D object which instances content |
|href | (HTML-piggybacked) metadata of a 3D object which links to content | |href | (HTML-piggybacked) metadata of a 3D object which links to content |
|filter | URI Fragment(s) which show/hide object(s) in a scene based on name/tag/property (`#q=cube&-price=>3`) | |filter | URI Fragment(s) which show/hide object(s) in a scene based on name/tag/property (`#cube&-price=>3`) |
|visual-meta | [visual-meta](https://visual.meta.info) data appended to text/books/papers which is indirectly visible/editable in XR. | |visual-meta | [visual-meta](https://visual.meta.info) data appended to text/books/papers which is indirectly visible/editable in XR. |
|requestless metadata | metadata which never spawns new requests (unlike RDF/HTML, which can cause framerate-dropping, hence not used a lot in games) | |requestless metadata | metadata which never spawns new requests (unlike RDF/HTML, which can cause framerate-dropping, hence not used a lot in games) |
|FPS | frames per second in spatial experiences (games,VR,AR e.g.), should be as high as possible | |FPS | frames per second in spatial experiences (games,VR,AR e.g.), should be as high as possible |
@ -952,6 +953,7 @@ This document has no IANA actions.
|extrospective | outward sensemaking ("I'm fairly sure John is a person who lives in oklahoma") | |extrospective | outward sensemaking ("I'm fairly sure John is a person who lives in oklahoma") |
|`◻` | ascii representation of an 3D object/mesh | |`◻` | ascii representation of an 3D object/mesh |
|(un)obtrusive | obtrusive: wrapping human text/thought in XML/HTML/JSON obfuscates human text into a salad of machine-symbols and words | |(un)obtrusive | obtrusive: wrapping human text/thought in XML/HTML/JSON obfuscates human text into a salad of machine-symbols and words |
|flat 3D object | a 3D object of which all verticies share a plane |
|BibTeX | simple tagging/citing/referencing standard for plaintext | |BibTeX | simple tagging/citing/referencing standard for plaintext |
|BibTag | a BibTeX tag | |BibTag | a BibTeX tag |
|(hashtag)bibs | an easy to speak/type/scan tagging SDL ([see here](https://github.com/coderofsalvation/hashtagbibs) which expands to BibTex/JSON/XML | |(hashtag)bibs | an easy to speak/type/scan tagging SDL ([see here](https://github.com/coderofsalvation/hashtagbibs) which expands to BibTex/JSON/XML |

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,8 @@ window.AFRAME.registerComponent('xrf', {
document.querySelector('[camera]').setAttribute('xrf-fade','') document.querySelector('[camera]').setAttribute('xrf-fade','')
AFRAME.fade = document.querySelector('[camera]').components['xrf-fade'] AFRAME.fade = document.querySelector('[camera]').components['xrf-fade']
if( document.location.host.match(/localhost/) ) document.querySelector('a-scene').setAttribute("stats",'')
document.querySelector('a-scene').addEventListener('loaded', () => { document.querySelector('a-scene').addEventListener('loaded', () => {
// enable XR fragments // enable XR fragments

View file

@ -62,12 +62,15 @@ pub.XRWG = (opts) => {
match.map( (w) => { match.map( (w) => {
if( w.key == `#${id}` ){ if( w.key == `#${id}` ){
if( w.value && w.value[0] == '#' ){ if( w.value && w.value[0] == '#' ){
frag = xrf.URI.parse( w.value )
v = Object.values(frag)[0]
// if value is alias, execute fragment value // if value is alias, execute fragment value
xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR ) xrf.hashbus.pub( w.value, xrf.model, xrf.XRF.METADATA | xrf.XRF.PV_OVERRIDE | xrf.XRF.NAVIGATOR )
xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene })
} }
} }
}) })
xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene }) if( !match.length ) xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene })
}else{ }else{
xrf.emit('dynamicKeyValue',{ ...opts,v,frag,id,match,scene }) xrf.emit('dynamicKeyValue',{ ...opts,v,frag,id,match,scene })
} }

View file

@ -7,7 +7,7 @@ xrf.addEventListener('dynamicKey', (opts) => {
let {scene,id,match,v} = opts let {scene,id,match,v} = opts
if( v.filter ){ if( v.filter ){
let frags = {} let frags = {}
frags[ v.fragment ] = v frags[ v.filter.key ] = v
xrf.filter.scene({frag:frags,scene}) xrf.filter.scene({frag:frags,scene})
} }
}) })
@ -23,7 +23,7 @@ xrf.filter = function(query, cb){
xrf.filter.scene = function(opts){ xrf.filter.scene = function(opts){
let {scene,frag} = opts let {scene,frag} = opts
console.dir(opts)
xrf.filter xrf.filter
.sort(frag) // get (sorted) filters from XR Fragments .sort(frag) // get (sorted) filters from XR Fragments
.process(frag,scene,opts) // show/hide things .process(frag,scene,opts) // show/hide things
@ -91,6 +91,9 @@ xrf.filter.process = function(frag,scene,opts){
let processed = {} let processed = {}
scene.traverse( (m) => { scene.traverse( (m) => {
if( filter.root && m.isSRC ) return // ignore src nodes when root is specific (#/VR #/AR e.g.)
// filter on value(expression) #foo=>3 e.g. // filter on value(expression) #foo=>3 e.g.
if( filter.value && m.userData[filter.key] ){ if( filter.value && m.userData[filter.key] ){
const visible = v.filter.testProperty(filter.key, m.userData[filter.key], filter.show === false ) const visible = v.filter.testProperty(filter.key, m.userData[filter.key], filter.show === false )

View file

@ -41,15 +41,14 @@ xrf.frag.href = function(v, opts){
.then( () => { .then( () => {
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA ) let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
// always keep a trail of last positions before we navigate // always commit current location (keep a trail of last positions before we navigate)
if( !document.location.hash.match(lastPos) ) xrf.navigator.to(`#${lastPos}`) if( !e.nocommit && !document.location.hash.match(lastPos) ) xrf.navigator.to(`#${lastPos}`)
xrf.navigator.to(v.string) // let's surf to HREF! xrf.navigator.to(v.string) // let's surf to HREF!
}) })
.catch( console.error ) .catch( console.error )
} }
let selected = mesh.userData.XRF.href.selected = (state) => () => { let selected = mesh.userData.XRF.href.selected = (state) => () => {
console.log("select "+mesh.name)
if( mesh.selected == state ) return // nothing changed if( mesh.selected == state ) return // nothing changed
xrf.interactive.objects.map( (o) => { xrf.interactive.objects.map( (o) => {
let newState = o.name == mesh.name ? state : false let newState = o.name == mesh.name ? state : false

View file

@ -102,7 +102,7 @@ xrf.portalNonEuclidian = function(opts){
// trigger href upon camera collide // trigger href upon camera collide
if( mesh.userData.XRF.href ){ if( mesh.userData.XRF.href ){
raycaster.far = 0.3 raycaster.far = 0.35
let cam = xrf.camera.getCam ? xrf.camera.getCam() : camera let cam = xrf.camera.getCam ? xrf.camera.getCam() : camera
cam.getWorldPosition(cameraPosition) cam.getWorldPosition(cameraPosition)
cam.getWorldDirection(cameraDirection) cam.getWorldDirection(cameraDirection)
@ -110,7 +110,7 @@ xrf.portalNonEuclidian = function(opts){
intersects = raycaster.intersectObjects([mesh], false) intersects = raycaster.intersectObjects([mesh], false)
if (intersects.length > 0 && !mesh.portal.teleporting ){ if (intersects.length > 0 && !mesh.portal.teleporting ){
mesh.portal.teleporting = true mesh.portal.teleporting = true
mesh.userData.XRF.href.exec() mesh.userData.XRF.href.exec({nocommit:true})
setTimeout( () => mesh.portal.teleporting = false, 500) // dont flip back and forth setTimeout( () => mesh.portal.teleporting = false, 500) // dont flip back and forth
} }
} }

View file

@ -52,11 +52,11 @@ class Filter {
private var str:String = ""; private var str:String = "";
private var q:haxe.DynamicAccess<Dynamic> = {}; // 1. create an associative array/object to store filter-arguments as objects private var q:haxe.DynamicAccess<Dynamic> = {}; // 1. create an associative array/object to store filter-arguments as objects
private var isProp:EReg = ~/^.*=[><=]?/; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` ) private var isProp:EReg = ~/^.*=[><=]?/; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` )
private var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` ) private var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` )
private var isRoot:EReg = ~/^[-]?\//; // 1. detect root selectors like `/foo` (reference regex= `/^[-]?\//` ) private var isRoot:EReg = ~/^[-]?\//; // 1. detect root selectors like `/foo` (reference regex= `/^[-]?\//` )
private var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` ) private var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` )
private var operators:EReg = ~/(^-|\*$)/; // 1. detect operators so you can easily strip keys (reference regex= `/(^-|\*$)/` ) private var operators:EReg = ~/(^-|\*$|\/)/; // 1. detect operators so you can easily strip keys (reference regex= `/(^-|\*$)/` )
private var isSelectorExclude:EReg = ~/^-/; // 1. detect exclude keys like `-foo` (reference regex= `/^-/` ) private var isSelectorExclude:EReg = ~/^-/; // 1. detect exclude keys like `-foo` (reference regex= `/^-/` )
public function new(str:String){ public function new(str:String){

View file

@ -9,7 +9,7 @@ import xrfragment.XRF;
class Parser { class Parser {
public static var error:String = ""; public static var error:String = "";
public static var debug:Bool = false; public static var debug:Bool = false;
public static var keyClean:EReg = ~/(\*$|^-)/g; public static var keyClean:EReg = ~/(\*$|^-|\/)/g;
@:keep @:keep
public static function parse(key:String,value:String,store:haxe.DynamicAccess<Dynamic>,?index:Int):Bool { public static function parse(key:String,value:String,store:haxe.DynamicAccess<Dynamic>,?index:Int):Bool {
@ -65,7 +65,7 @@ class Parser {
trace(" fragment '"+key+"' has incompatible value ("+value+")");// 1. don't add to store if value-type is incorrect trace(" fragment '"+key+"' has incompatible value ("+value+")");// 1. don't add to store if value-type is incorrect
return false; return false;
} }
store.set(key, v ); // 1. if valid, add to store store.set( keyClean.replace(key,''), v); // 1. if valid, add to store
if( debug ) trace(" "+key+": "+v.string); if( debug ) trace(" "+key+": "+v.string);
}else{ // 1. expose (but mark) non-offical fragments too }else{ // 1. expose (but mark) non-offical fragments too
if( Std.isOfType(value, String) ) v.guessType(v,value); if( Std.isOfType(value, String) ) v.guessType(v,value);