work in progress [might break]
This commit is contained in:
parent
f11a647bb7
commit
cc3f58f493
|
@ -512,6 +512,8 @@ It's simple but powerful syntax which allows filtering the scene using searcheng
|
||||||
|
|
||||||
> \* = `#-/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 |
|
> \* = `#-/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 |
|
||||||
|
|
||||||
|
Nested selection is always implied (there's no `*` `>`/`<` css-like operators on purpose) which keeps XR Fragments easy to implement, and still allows fine-grained control chaining nested selectors (`#-sky&house&-table` e.g.).
|
||||||
|
|
||||||
[» 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)
|
||||||
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/3)
|
[» discussion](https://github.com/coderofsalvation/xrfragment/issues/3)
|
||||||
|
|
Binary file not shown.
|
@ -35,6 +35,8 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
|
||||||
pub.fragment = (k, opts ) => { // evaluate one fragment
|
pub.fragment = (k, opts ) => { // evaluate one fragment
|
||||||
let frag = opts.frag[k];
|
let frag = opts.frag[k];
|
||||||
|
|
||||||
|
if( frag.is( xrf.XRF.PV_EXECUTE ) ) pub.XRWG({...opts,frag})
|
||||||
|
|
||||||
// call native function (xrf/env.js e.g.), or pass it to user decorator
|
// call native function (xrf/env.js e.g.), or pass it to user decorator
|
||||||
xrf.emit(k,opts)
|
xrf.emit(k,opts)
|
||||||
.then( () => {
|
.then( () => {
|
||||||
|
@ -46,6 +48,7 @@ pub.fragment = (k, opts ) => { // evaluate one fragment
|
||||||
|
|
||||||
pub.XRWG = (opts) => {
|
pub.XRWG = (opts) => {
|
||||||
let {frag,scene,model,renderer} = opts
|
let {frag,scene,model,renderer} = opts
|
||||||
|
console.dir(opts)
|
||||||
|
|
||||||
// if this query was triggered by an src-value, lets filter it
|
// if this query was triggered by an src-value, lets filter it
|
||||||
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
||||||
|
@ -62,15 +65,12 @@ 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 })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if( !match.length ) xrf.emit('dynamicKey',{ ...opts,v,frag,id,match,scene })
|
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 })
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -45,7 +45,7 @@ xrf.filter.process = function(frag,scene,opts){
|
||||||
const hasName = (m,name,filter) => m.name == name
|
const hasName = (m,name,filter) => m.name == name
|
||||||
const hasNameOrTag = (m,name_or_tag,filter) => hasName(m,name_or_tag) ||
|
const hasNameOrTag = (m,name_or_tag,filter) => hasName(m,name_or_tag) ||
|
||||||
String(m.userData['tag']).match( new RegExp("(^| )"+name_or_tag) )
|
String(m.userData['tag']).match( new RegExp("(^| )"+name_or_tag) )
|
||||||
const cleanupKey = (k) => k.replace(/[-\*]/g,'')
|
const cleanupKey = (k) => k.replace(/[-\*\/]/g,'')
|
||||||
|
|
||||||
let firstFilter = frag.filters.length ? frag.filters[0].filter.get() : false
|
let firstFilter = frag.filters.length ? frag.filters[0].filter.get() : false
|
||||||
let showers = frag.filters.filter( (v) => v.filter.get().show === true )
|
let showers = frag.filters.filter( (v) => v.filter.get().show === true )
|
||||||
|
@ -84,6 +84,12 @@ xrf.filter.process = function(frag,scene,opts){
|
||||||
if( processed ) processed[n.uuid] == true
|
if( processed ) processed[n.uuid] == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const insideSRC = (m) => {
|
||||||
|
let src = false
|
||||||
|
m.traverseAncestors( (n) => n.isSRC ? src = true : false )
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
// then show/hide things based on secondary selectors
|
// then show/hide things based on secondary selectors
|
||||||
frag.filters.map( (v) => {
|
frag.filters.map( (v) => {
|
||||||
const filter = v.filter.get()
|
const filter = v.filter.get()
|
||||||
|
@ -91,19 +97,21 @@ xrf.filter.process = function(frag,scene,opts){
|
||||||
let processed = {}
|
let processed = {}
|
||||||
|
|
||||||
scene.traverse( (m) => {
|
scene.traverse( (m) => {
|
||||||
|
// filter on value(expression) #foo=>3 e.g. *TODO* do this in XRWG
|
||||||
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.
|
|
||||||
if( filter.value && m.userData[filter.key] ){
|
if( filter.value && m.userData[filter.key] ){
|
||||||
|
if( filter.root && insideSRC(m) ) return
|
||||||
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 )
|
||||||
setVisible(m,visible,processed)
|
setVisible(m,visible,processed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// include/exclude object(s) when id/tag matches (#foo or #-foo e.g.)
|
})
|
||||||
if( hasNameOrTag(m,name_or_tag,filter) ){
|
// include/exclude object(s) when id/tag matches (#foo or #-foo e.g.)
|
||||||
setVisible(m,filter.show)
|
let matches = xrf.XRWG.match(name_or_tag)
|
||||||
}
|
matches.map( (match) => {
|
||||||
|
match.nodes.map( (node) => {
|
||||||
|
if( filter.root && insideSRC(node) ) return
|
||||||
|
setVisible(node,filter.show)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ xrf.frag.src.addModel = (model,url,frag,opts) => {
|
||||||
let {mesh} = opts
|
let {mesh} = opts
|
||||||
let scene = model.scene
|
let scene = model.scene
|
||||||
xrf.frag.src.filterScene(scene,{...opts,frag}) // filter scene
|
xrf.frag.src.filterScene(scene,{...opts,frag}) // filter scene
|
||||||
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) // mark everything isSRC & isXRF
|
|
||||||
if( mesh.material ) mesh.material.visible = false // hide placeholder object
|
if( mesh.material ) mesh.material.visible = false // hide placeholder object
|
||||||
|
mesh.traverse( (n) => n.isSRC = n.isXRF = true ) // mark everything isSRC & isXRF
|
||||||
//enableSourcePortation(scene)
|
//enableSourcePortation(scene)
|
||||||
if( xrf.frag.src.renderAsPortal(mesh) ){
|
if( xrf.frag.src.renderAsPortal(mesh) ){
|
||||||
if( !opts.isLocal ) xrf.scene.add(scene)
|
if( !opts.isLocal ) xrf.scene.add(scene)
|
||||||
|
|
|
@ -15,11 +15,11 @@ let loadVideo = (mimetype) => function(url,opts){
|
||||||
mat.map = texture
|
mat.map = texture
|
||||||
mesh.material = mat
|
mesh.material = mat
|
||||||
// set range
|
// set range
|
||||||
video.addEventListener('timeupdate', function timeupdate() {
|
//video.addEventListener('timeupdate', function timeupdate() {
|
||||||
if (video.t && video.currentTime < video.t.y || video.currentTime >= video.t.z ) {
|
// if (frag.t && video.currentTime < frag.t.y || video.currentTime >= frag.t.z ) {
|
||||||
vid.currentTime = video.t.y
|
// video.currentTime = frag.t.y
|
||||||
}
|
// }
|
||||||
},false)
|
//},false)
|
||||||
})
|
})
|
||||||
|
|
||||||
video.src = url
|
video.src = url
|
||||||
|
|
|
@ -34,6 +34,7 @@ xrf.addEventListener('parseModel', (opts) => {
|
||||||
|
|
||||||
model.animations.map( (anim) => {
|
model.animations.map( (anim) => {
|
||||||
anim.optimize()
|
anim.optimize()
|
||||||
|
console.log("action: "+anim.name)
|
||||||
mixer.actions.push( mixer.clipAction( anim, model.scene ) )
|
mixer.actions.push( mixer.clipAction( anim, model.scene ) )
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Filter {
|
||||||
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){
|
||||||
|
|
|
@ -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 = ~/(^-)?(\/)?/; // 1. detect - and / operators so you can easily strip keys (reference regex= ~/(^-)?(\/)?/; )
|
||||||
|
|
||||||
@: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 {
|
||||||
|
@ -53,7 +53,7 @@ class Parser {
|
||||||
var isPVDefault:Bool = value.length == 0 && key.length > 0 && key == "#";
|
var isPVDefault:Bool = value.length == 0 && key.length > 0 && key == "#";
|
||||||
if( isPVDynamic ){ //|| isPVDefault ){ // 1. add keys without values to store as [predefined view](predefined_view)
|
if( isPVDynamic ){ //|| isPVDefault ){ // 1. add keys without values to store as [predefined view](predefined_view)
|
||||||
var v:XRF = new XRF(key, XRF.PV_EXECUTE | XRF.NAVIGATOR, index );
|
var v:XRF = new XRF(key, XRF.PV_EXECUTE | XRF.NAVIGATOR, index );
|
||||||
v.validate(value); // will fail but will parse multiple args for us (separated by |)
|
v.validate(value); // ignore failures (empty values are allowed)
|
||||||
store.set( keyClean.replace(key,''), v );
|
store.set( keyClean.replace(key,''), v );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ createScene = (noadd) => {
|
||||||
b.userData.score = 2
|
b.userData.score = 2
|
||||||
b.userData.tag = "foo bar"
|
b.userData.tag = "foo bar"
|
||||||
c.userData.tag = "flop flap"
|
c.userData.tag = "flop flap"
|
||||||
|
a.userData.tag = "VR"
|
||||||
a.userData.price = 1
|
a.userData.price = 1
|
||||||
b.userData.price = 5
|
b.userData.price = 5
|
||||||
c.userData.price = 10
|
c.userData.price = 10
|
||||||
|
@ -103,3 +104,7 @@ console.assert( test(), {scn,reason:`tagfilter: #-b&-foo&bar&flop&-bar&flop"`})
|
||||||
scn = filterScene("#-price&price=>5")
|
scn = filterScene("#-price&price=>5")
|
||||||
test = () => scn.visible("a",false,true) && scn.visible("b",true) && scn.visible("c",true)
|
test = () => scn.visible("a",false,true) && scn.visible("b",true) && scn.visible("c",true)
|
||||||
console.assert( test(), {scn,reason:`tagfilter: #-price&price=>5"`})
|
console.assert( test(), {scn,reason:`tagfilter: #-price&price=>5"`})
|
||||||
|
|
||||||
|
scn = filterScene("#-/VR&b")
|
||||||
|
test = () => scn.visible("a",false,true) && scn.visible("b",true) && scn.visible("c",true)
|
||||||
|
console.assert( test(), {scn,reason:`tagfilter: #-/VR&b"`})
|
||||||
|
|
Loading…
Reference in New Issue