huge refactor because of xrmacro vs xrfragment separation
This commit is contained in:
parent
3e3724c950
commit
ed5ecc5be6
|
@ -266,7 +266,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -756,9 +757,11 @@ xrf.parseModel = function(model,url){
|
|||
xrf.getLastModel = () => xrf.model.last
|
||||
|
||||
xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
||||
if( !url ) return
|
||||
if( !url.match(/#/) ) url = `#${url}`
|
||||
model = model || xrf.model
|
||||
let { THREE, camera } = xrf
|
||||
let frag = xrf.URI.parse( url, flags || xrf.XRF.NAVIGATOR )
|
||||
let frag = xrf.URI.parse( url, flags != undefined ? flags : xrf.XRF.NAVIGATOR )
|
||||
let opts = {frag, mesh:xrf.camera, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
xrf.emit('eval',opts)
|
||||
.then( () => {
|
||||
|
@ -766,6 +769,7 @@ xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
|||
xrf.eval.fragment(k,opts)
|
||||
}
|
||||
})
|
||||
return frag
|
||||
}
|
||||
|
||||
xrf.eval.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) inside mesh of model
|
||||
|
@ -968,6 +972,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
|
||||
if( !file || xrf.model.file == file ){ // we're already loaded
|
||||
xrf.eval( url, xrf.model, flags ) // and eval local URI XR fragments
|
||||
xrf.navigator.updateHash(hash)
|
||||
return resolve(xrf.model)
|
||||
}
|
||||
|
||||
|
@ -993,8 +998,10 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
|
||||
xrf.eval( url, model ) // and eval URI XR fragments
|
||||
xrf.add( model.scene )
|
||||
if( !hash.match(/pos=/) )
|
||||
if( !hash.match(/pos=/) ){
|
||||
xrf.eval( '#pos=0,0,0' ) // set default position if not specified
|
||||
}
|
||||
xrf.navigator.updateHash(hash)
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
|
@ -1026,13 +1033,181 @@ xrf.navigator.pushState = (file,hash) => {
|
|||
console.log("pushstate")
|
||||
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
|
||||
}
|
||||
xrf.frag.bg = function(v, opts){
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.bg ){
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.env && !scene.environment ){
|
||||
let env = mesh.getObjectByName(frag.env.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${frag.env.string}' as environment map`)
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.fog ){
|
||||
let v = frag.fog
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
})
|
||||
xrf.macros = {}
|
||||
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
for( let k in frag ){
|
||||
let id = mesh.name+"_"+k
|
||||
let fragment = frag[k]
|
||||
|
||||
if( k.match(/^!/) ){
|
||||
if( mesh.material) mesh.material = mesh.material.clone()
|
||||
if( mesh.isSRC || scene.isSRC ) return; // dont allow recursion for now
|
||||
|
||||
if( xrf.macros[k] ) return // already initialized
|
||||
|
||||
console.log("└ initing xrmacro: "+k)
|
||||
xrf.macros[k] = fragment
|
||||
fragment.args = fragment.string.split("|")
|
||||
|
||||
fragment.trigger = (e) => {
|
||||
xrf
|
||||
.emit('macro',{click:true,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => {
|
||||
rrFrag = fragment.args[ xrf.roundrobin( fragment,model) ]
|
||||
console.log("└ xrmacro: "+rrFrag)
|
||||
if( xrf.macros[ rrFrag ] ){
|
||||
xrf.macros[ rrFrag ].trigger()
|
||||
} else {
|
||||
if( rrFrag[0] == '#' ) xrf.navigator.updateHash(rrFrag)
|
||||
else xrf.eval(rrFrag,null,0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let selected = (state) => () => {
|
||||
if( mesh.selected == state ) return // nothing changed
|
||||
if( mesh.material ){
|
||||
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
|
||||
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
|
||||
}
|
||||
// update mouse cursor
|
||||
if( !renderer.domElement.lastCursor )
|
||||
renderer.domElement.lastCursor = renderer.domElement.style.cursor
|
||||
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
|
||||
xrf
|
||||
.emit('macro',{selected:state,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => mesh.selected = state )
|
||||
}
|
||||
|
||||
mesh.addEventListener('click', fragment.trigger )
|
||||
mesh.addEventListener('mousemove', selected(true) )
|
||||
mesh.addEventListener('nocollide', selected(false) )
|
||||
|
||||
// lazy add mesh to interactive group (because we're inside a recursive traverse)
|
||||
setTimeout( (mesh) => {
|
||||
const world = {
|
||||
pos: new THREE.Vector3(),
|
||||
scale: new THREE.Vector3(),
|
||||
quat: new THREE.Quaternion()
|
||||
}
|
||||
mesh.getWorldPosition(world.pos)
|
||||
mesh.getWorldScale(world.scale)
|
||||
mesh.getWorldQuaternion(world.quat);
|
||||
mesh.position.copy(world.pos)
|
||||
mesh.scale.copy(world.scale)
|
||||
mesh.setRotationFromQuaternion(world.quat);
|
||||
xrf.interactive.add(mesh)
|
||||
}, 10, mesh )
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.mov && frag.q ){
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
},10, frag.mov )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.pos && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.rot && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.scale && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.show && frag.q ){
|
||||
let show = frag.show
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( show.args ) v = show.args[ xrf.roundrobin(show,model) ]
|
||||
else v = show.int
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}, 20, v)
|
||||
}
|
||||
})
|
||||
xrf.frag.clip = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1043,26 +1218,6 @@ xrf.frag.clip = function(v, opts){
|
|||
camera.far = v.y
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
xrf.frag.env = function(v, opts){
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let env = mesh.getObjectByName(v.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${v.string}' as environment map`)
|
||||
}
|
||||
xrf.frag.fog = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
xrf.frag.fov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1168,9 +1323,10 @@ xrf.frag.href = function(v, opts){
|
|||
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
|
||||
.then( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
//const flags = v.string[0] == '#' && v.string.match(/(\||#q)/) ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}` // always commit last position
|
||||
console.log(v.string)
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string,flags) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1217,36 +1373,11 @@ xrf.frag.href = function(v, opts){
|
|||
*
|
||||
* > capture of <a href="./example/aframe/sandbox" target="_blank">aframe/sandbox</a>
|
||||
*/
|
||||
xrf.frag.mov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.pos = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}else{
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
xrf.frag.defaultPredefinedView = (opts) => {
|
||||
let {scene,model} = opts;
|
||||
|
@ -1295,7 +1426,7 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
let opts = {frag, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
if( frag[k].is( xrf.XRF.PV_EXECUTE ) && scene.XRF_PV_ORIGIN != k ){ // cyclic detection
|
||||
traverseScene(frag[k],scene) // recurse predefined views
|
||||
}else xrf.eval.fragment(k,opts)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1313,27 +1444,28 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
})
|
||||
}
|
||||
|
||||
let pviews = []
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,xrf.model) ]
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
if( v.string ) pviews.push(v.string)
|
||||
}else if( v.is( xrf.XRF.NAVIGATOR ) ) pviews.push(`${i}=${v.string}`)
|
||||
// if this query was triggered by an src-value, lets filter it
|
||||
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
||||
if( isSRC ){ // spec : https://xrfragment.org/#src
|
||||
console.log("filtering predefined view of src")
|
||||
console.dir(frag)
|
||||
}else{
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pviews.length ) xrf.navigator.updateHash( pviews.join("&") )
|
||||
}
|
||||
|
||||
// react to url changes
|
||||
//xrf.addEventListener('updateHash', (opts) => {
|
||||
// console.log("update hash");
|
||||
// console.dir(opts)
|
||||
// let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
//})
|
||||
xrf.addEventListener('updateHash', (opts) => {
|
||||
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
})
|
||||
|
||||
// clicking href url with predefined view
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
|
@ -1371,111 +1503,33 @@ xrf.frag.q = function(v, opts){
|
|||
return o
|
||||
})
|
||||
}
|
||||
xrf.frag.q.filter(scene,frag) // spec : https://xrfragment.org/#queries
|
||||
}
|
||||
|
||||
// spec: https://xrfragment.org/#src
|
||||
const instanceScene = () => {
|
||||
v.scene = new THREE.Group()
|
||||
for ( let i in v.query ) {
|
||||
let target = v.query[i]
|
||||
if( !scene.getObjectByName(i) && i != '*' ) return console.log(` └ mesh not found: ${i}`)
|
||||
if( i == '*' ){
|
||||
let cloneScene = scene.clone()
|
||||
// add interactive elements (href's e.g.) *TODO* this is called by both internal/external src's
|
||||
v.scene.add( xrf.interactive.clone() )
|
||||
cloneScene.children.forEach( (child) => v.scene.getObjectByName(child.name) ? null : v.scene.add(child) )
|
||||
target.mesh = v.scene
|
||||
}else{
|
||||
if( !v.scene.getObjectByName(i) && target.id === true ){
|
||||
console.log(` └ query-ing mesh: ${i}`)
|
||||
v.scene.add( target.mesh = scene.getObjectByName(i).clone() )
|
||||
}
|
||||
}
|
||||
if( target.id != undefined && target.mesh ){
|
||||
target.mesh.position.set(0,0,0)
|
||||
target.mesh.rotation.set(0,0,0)
|
||||
}
|
||||
}
|
||||
// hide negative selectors
|
||||
let negative = []
|
||||
v.scene.traverse( (mesh) => {
|
||||
for ( let i in v.query ) {
|
||||
if( mesh.name == i && v.query[i].id === false ) negative.push(mesh)
|
||||
}
|
||||
})
|
||||
negative.map( (mesh) => mesh.visible = false )
|
||||
}
|
||||
|
||||
xrf.frag.q.filter = function(scene,frag){
|
||||
// spec: https://xrfragment.org/#queries
|
||||
const showHide = () => {
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const doLocalInstancing = opts.embedded && opts.embedded.fragment == 'src' && opts.embedded.string[0] == '#'
|
||||
if( doLocalInstancing ) instanceScene() // spec : https://xrfragment.org/#src
|
||||
else showHide() // spec : https://xrfragment.org/#queries
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
xrf.frag.rot = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}else{
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
}
|
||||
xrf.frag.scale = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.show = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}
|
||||
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
// *TODO* use webgl instancing
|
||||
|
||||
|
@ -1489,27 +1543,29 @@ xrf.frag.src = function(v, opts){
|
|||
let frag = xrfragment.URI.parse(v.string)
|
||||
|
||||
const localSRC = () => {
|
||||
|
||||
setTimeout( () => {
|
||||
// scale URI XR Fragments inside src-value
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone() )
|
||||
xrf.eval.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( frag.q.query ){
|
||||
let srcScene = frag.q.scene // three/xrf/q.js initializes .scene
|
||||
if( !srcScene || !srcScene.visible ) return
|
||||
console.log(" └ inserting "+i+" (srcScene)")
|
||||
srcScene.position.set(0,0,0)
|
||||
srcScene.rotation.set(0,0,0)
|
||||
srcScene.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ;//delete m.userData.src // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true})
|
||||
})
|
||||
if( srcScene.visible ) src.add( srcScene )
|
||||
}
|
||||
xrf.frag.src.scale( src, opts )
|
||||
},10)
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone();
|
||||
src.isSRC = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
|
@ -1525,8 +1581,8 @@ xrf.frag.src = function(v, opts){
|
|||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) localSRC() // current file
|
||||
else externalSRC() // external file
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
|
@ -1772,7 +1828,7 @@ window.AFRAME.registerComponent('xrf-button', {
|
|||
el.setAttribute('material', {
|
||||
color: this.color,
|
||||
transparent:true,
|
||||
opacity:0.3
|
||||
opacity:0.5
|
||||
});
|
||||
el.setAttribute('pressable', '');
|
||||
labelEl.setAttribute('position', '0 0 0.01');
|
||||
|
|
|
@ -266,7 +266,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -1633,7 +1633,8 @@ __xrfragment_Parser.parse = function(key,value,store)
|
|||
if (__lua_Boot.__instanceof(value, String)) then
|
||||
v:guessType(v, value);
|
||||
end;
|
||||
store[Std.string("_") .. Std.string(key)] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
end;
|
||||
do return true end;
|
||||
end
|
||||
|
|
|
@ -266,7 +266,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -1339,8 +1339,8 @@ class xrfragment_Parser:
|
|||
else:
|
||||
if Std.isOfType(value,str):
|
||||
v.guessType(v,value)
|
||||
key1 = ("_" + ("null" if key is None else key))
|
||||
setattr(store,(("_hx_" + key1) if ((key1 in python_Boot.keywords)) else (("_hx_" + key1) if (((((len(key1) > 2) and ((ord(key1[0]) == 95))) and ((ord(key1[1]) == 95))) and ((ord(key1[(len(key1) - 1)]) != 95)))) else key1)),v)
|
||||
v.noXRF = True
|
||||
setattr(store,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),v)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -1584,12 +1584,13 @@ class xrfragment_URI:
|
|||
|
||||
class xrfragment_XRF:
|
||||
_hx_class_name = "xrfragment.XRF"
|
||||
__slots__ = ("fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query")
|
||||
_hx_fields = ["fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query"]
|
||||
__slots__ = ("fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query", "noXRF")
|
||||
_hx_fields = ["fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query", "noXRF"]
|
||||
_hx_methods = ["is", "validate", "guessType"]
|
||||
_hx_statics = ["ASSET", "PROP_BIND", "QUERY_OPERATOR", "PROMPT", "ROUNDROBIN", "NAVIGATOR", "METADATA", "PV_OVERRIDE", "PV_EXECUTE", "T_COLOR", "T_INT", "T_FLOAT", "T_VECTOR2", "T_VECTOR3", "T_URL", "T_PREDEFINED_VIEW", "T_STRING", "T_STRING_OBJ", "T_STRING_OBJ_PROP", "isColor", "isInt", "isFloat", "isVector", "isUrl", "isUrlOrPretypedView", "isString", "set", "unset"]
|
||||
|
||||
def __init__(self,_fragment,_flags):
|
||||
self.noXRF = None
|
||||
self.query = None
|
||||
self.args = None
|
||||
self.float = None
|
||||
|
|
|
@ -266,7 +266,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -756,9 +757,11 @@ xrf.parseModel = function(model,url){
|
|||
xrf.getLastModel = () => xrf.model.last
|
||||
|
||||
xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
||||
if( !url ) return
|
||||
if( !url.match(/#/) ) url = `#${url}`
|
||||
model = model || xrf.model
|
||||
let { THREE, camera } = xrf
|
||||
let frag = xrf.URI.parse( url, flags || xrf.XRF.NAVIGATOR )
|
||||
let frag = xrf.URI.parse( url, flags != undefined ? flags : xrf.XRF.NAVIGATOR )
|
||||
let opts = {frag, mesh:xrf.camera, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
xrf.emit('eval',opts)
|
||||
.then( () => {
|
||||
|
@ -766,6 +769,7 @@ xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
|||
xrf.eval.fragment(k,opts)
|
||||
}
|
||||
})
|
||||
return frag
|
||||
}
|
||||
|
||||
xrf.eval.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) inside mesh of model
|
||||
|
@ -968,6 +972,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
|
||||
if( !file || xrf.model.file == file ){ // we're already loaded
|
||||
xrf.eval( url, xrf.model, flags ) // and eval local URI XR fragments
|
||||
xrf.navigator.updateHash(hash)
|
||||
return resolve(xrf.model)
|
||||
}
|
||||
|
||||
|
@ -993,8 +998,10 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
|
||||
xrf.eval( url, model ) // and eval URI XR fragments
|
||||
xrf.add( model.scene )
|
||||
if( !hash.match(/pos=/) )
|
||||
if( !hash.match(/pos=/) ){
|
||||
xrf.eval( '#pos=0,0,0' ) // set default position if not specified
|
||||
}
|
||||
xrf.navigator.updateHash(hash)
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
|
@ -1026,13 +1033,181 @@ xrf.navigator.pushState = (file,hash) => {
|
|||
console.log("pushstate")
|
||||
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
|
||||
}
|
||||
xrf.frag.bg = function(v, opts){
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.bg ){
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.env && !scene.environment ){
|
||||
let env = mesh.getObjectByName(frag.env.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${frag.env.string}' as environment map`)
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.fog ){
|
||||
let v = frag.fog
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
})
|
||||
xrf.macros = {}
|
||||
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
for( let k in frag ){
|
||||
let id = mesh.name+"_"+k
|
||||
let fragment = frag[k]
|
||||
|
||||
if( k.match(/^!/) ){
|
||||
if( mesh.material) mesh.material = mesh.material.clone()
|
||||
if( mesh.isSRC || scene.isSRC ) return; // dont allow recursion for now
|
||||
|
||||
if( xrf.macros[k] ) return // already initialized
|
||||
|
||||
console.log("└ initing xrmacro: "+k)
|
||||
xrf.macros[k] = fragment
|
||||
fragment.args = fragment.string.split("|")
|
||||
|
||||
fragment.trigger = (e) => {
|
||||
xrf
|
||||
.emit('macro',{click:true,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => {
|
||||
rrFrag = fragment.args[ xrf.roundrobin( fragment,model) ]
|
||||
console.log("└ xrmacro: "+rrFrag)
|
||||
if( xrf.macros[ rrFrag ] ){
|
||||
xrf.macros[ rrFrag ].trigger()
|
||||
} else {
|
||||
if( rrFrag[0] == '#' ) xrf.navigator.updateHash(rrFrag)
|
||||
else xrf.eval(rrFrag,null,0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let selected = (state) => () => {
|
||||
if( mesh.selected == state ) return // nothing changed
|
||||
if( mesh.material ){
|
||||
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
|
||||
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
|
||||
}
|
||||
// update mouse cursor
|
||||
if( !renderer.domElement.lastCursor )
|
||||
renderer.domElement.lastCursor = renderer.domElement.style.cursor
|
||||
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
|
||||
xrf
|
||||
.emit('macro',{selected:state,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => mesh.selected = state )
|
||||
}
|
||||
|
||||
mesh.addEventListener('click', fragment.trigger )
|
||||
mesh.addEventListener('mousemove', selected(true) )
|
||||
mesh.addEventListener('nocollide', selected(false) )
|
||||
|
||||
// lazy add mesh to interactive group (because we're inside a recursive traverse)
|
||||
setTimeout( (mesh) => {
|
||||
const world = {
|
||||
pos: new THREE.Vector3(),
|
||||
scale: new THREE.Vector3(),
|
||||
quat: new THREE.Quaternion()
|
||||
}
|
||||
mesh.getWorldPosition(world.pos)
|
||||
mesh.getWorldScale(world.scale)
|
||||
mesh.getWorldQuaternion(world.quat);
|
||||
mesh.position.copy(world.pos)
|
||||
mesh.scale.copy(world.scale)
|
||||
mesh.setRotationFromQuaternion(world.quat);
|
||||
xrf.interactive.add(mesh)
|
||||
}, 10, mesh )
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.mov && frag.q ){
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
},10, frag.mov )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.pos && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.rot && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.scale && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.show && frag.q ){
|
||||
let show = frag.show
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( show.args ) v = show.args[ xrf.roundrobin(show,model) ]
|
||||
else v = show.int
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}, 20, v)
|
||||
}
|
||||
})
|
||||
xrf.frag.clip = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1043,26 +1218,6 @@ xrf.frag.clip = function(v, opts){
|
|||
camera.far = v.y
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
xrf.frag.env = function(v, opts){
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let env = mesh.getObjectByName(v.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${v.string}' as environment map`)
|
||||
}
|
||||
xrf.frag.fog = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
xrf.frag.fov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1168,9 +1323,10 @@ xrf.frag.href = function(v, opts){
|
|||
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
|
||||
.then( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
//const flags = v.string[0] == '#' && v.string.match(/(\||#q)/) ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}` // always commit last position
|
||||
console.log(v.string)
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string,flags) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1217,36 +1373,11 @@ xrf.frag.href = function(v, opts){
|
|||
*
|
||||
* > capture of <a href="./example/aframe/sandbox" target="_blank">aframe/sandbox</a>
|
||||
*/
|
||||
xrf.frag.mov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.pos = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}else{
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
xrf.frag.defaultPredefinedView = (opts) => {
|
||||
let {scene,model} = opts;
|
||||
|
@ -1295,7 +1426,7 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
let opts = {frag, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
if( frag[k].is( xrf.XRF.PV_EXECUTE ) && scene.XRF_PV_ORIGIN != k ){ // cyclic detection
|
||||
traverseScene(frag[k],scene) // recurse predefined views
|
||||
}else xrf.eval.fragment(k,opts)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1313,27 +1444,28 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
})
|
||||
}
|
||||
|
||||
let pviews = []
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,xrf.model) ]
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
if( v.string ) pviews.push(v.string)
|
||||
}else if( v.is( xrf.XRF.NAVIGATOR ) ) pviews.push(`${i}=${v.string}`)
|
||||
// if this query was triggered by an src-value, lets filter it
|
||||
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
||||
if( isSRC ){ // spec : https://xrfragment.org/#src
|
||||
console.log("filtering predefined view of src")
|
||||
console.dir(frag)
|
||||
}else{
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pviews.length ) xrf.navigator.updateHash( pviews.join("&") )
|
||||
}
|
||||
|
||||
// react to url changes
|
||||
//xrf.addEventListener('updateHash', (opts) => {
|
||||
// console.log("update hash");
|
||||
// console.dir(opts)
|
||||
// let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
//})
|
||||
xrf.addEventListener('updateHash', (opts) => {
|
||||
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
})
|
||||
|
||||
// clicking href url with predefined view
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
|
@ -1371,111 +1503,33 @@ xrf.frag.q = function(v, opts){
|
|||
return o
|
||||
})
|
||||
}
|
||||
xrf.frag.q.filter(scene,frag) // spec : https://xrfragment.org/#queries
|
||||
}
|
||||
|
||||
// spec: https://xrfragment.org/#src
|
||||
const instanceScene = () => {
|
||||
v.scene = new THREE.Group()
|
||||
for ( let i in v.query ) {
|
||||
let target = v.query[i]
|
||||
if( !scene.getObjectByName(i) && i != '*' ) return console.log(` └ mesh not found: ${i}`)
|
||||
if( i == '*' ){
|
||||
let cloneScene = scene.clone()
|
||||
// add interactive elements (href's e.g.) *TODO* this is called by both internal/external src's
|
||||
v.scene.add( xrf.interactive.clone() )
|
||||
cloneScene.children.forEach( (child) => v.scene.getObjectByName(child.name) ? null : v.scene.add(child) )
|
||||
target.mesh = v.scene
|
||||
}else{
|
||||
if( !v.scene.getObjectByName(i) && target.id === true ){
|
||||
console.log(` └ query-ing mesh: ${i}`)
|
||||
v.scene.add( target.mesh = scene.getObjectByName(i).clone() )
|
||||
}
|
||||
}
|
||||
if( target.id != undefined && target.mesh ){
|
||||
target.mesh.position.set(0,0,0)
|
||||
target.mesh.rotation.set(0,0,0)
|
||||
}
|
||||
}
|
||||
// hide negative selectors
|
||||
let negative = []
|
||||
v.scene.traverse( (mesh) => {
|
||||
for ( let i in v.query ) {
|
||||
if( mesh.name == i && v.query[i].id === false ) negative.push(mesh)
|
||||
}
|
||||
})
|
||||
negative.map( (mesh) => mesh.visible = false )
|
||||
}
|
||||
|
||||
xrf.frag.q.filter = function(scene,frag){
|
||||
// spec: https://xrfragment.org/#queries
|
||||
const showHide = () => {
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const doLocalInstancing = opts.embedded && opts.embedded.fragment == 'src' && opts.embedded.string[0] == '#'
|
||||
if( doLocalInstancing ) instanceScene() // spec : https://xrfragment.org/#src
|
||||
else showHide() // spec : https://xrfragment.org/#queries
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
xrf.frag.rot = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}else{
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
}
|
||||
xrf.frag.scale = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.show = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}
|
||||
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
// *TODO* use webgl instancing
|
||||
|
||||
|
@ -1489,27 +1543,29 @@ xrf.frag.src = function(v, opts){
|
|||
let frag = xrfragment.URI.parse(v.string)
|
||||
|
||||
const localSRC = () => {
|
||||
|
||||
setTimeout( () => {
|
||||
// scale URI XR Fragments inside src-value
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone() )
|
||||
xrf.eval.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( frag.q.query ){
|
||||
let srcScene = frag.q.scene // three/xrf/q.js initializes .scene
|
||||
if( !srcScene || !srcScene.visible ) return
|
||||
console.log(" └ inserting "+i+" (srcScene)")
|
||||
srcScene.position.set(0,0,0)
|
||||
srcScene.rotation.set(0,0,0)
|
||||
srcScene.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ;//delete m.userData.src // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true})
|
||||
})
|
||||
if( srcScene.visible ) src.add( srcScene )
|
||||
}
|
||||
xrf.frag.src.scale( src, opts )
|
||||
},10)
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone();
|
||||
src.isSRC = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
|
@ -1525,8 +1581,8 @@ xrf.frag.src = function(v, opts){
|
|||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) localSRC() // current file
|
||||
else externalSRC() // external file
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
|
|
|
@ -266,7 +266,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -756,9 +757,11 @@ xrf.parseModel = function(model,url){
|
|||
xrf.getLastModel = () => xrf.model.last
|
||||
|
||||
xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
||||
if( !url ) return
|
||||
if( !url.match(/#/) ) url = `#${url}`
|
||||
model = model || xrf.model
|
||||
let { THREE, camera } = xrf
|
||||
let frag = xrf.URI.parse( url, flags || xrf.XRF.NAVIGATOR )
|
||||
let frag = xrf.URI.parse( url, flags != undefined ? flags : xrf.XRF.NAVIGATOR )
|
||||
let opts = {frag, mesh:xrf.camera, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
xrf.emit('eval',opts)
|
||||
.then( () => {
|
||||
|
@ -766,6 +769,7 @@ xrf.eval = function( url, model, flags ){ // evaluate fragments in url
|
|||
xrf.eval.fragment(k,opts)
|
||||
}
|
||||
})
|
||||
return frag
|
||||
}
|
||||
|
||||
xrf.eval.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) inside mesh of model
|
||||
|
@ -968,6 +972,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
|
||||
if( !file || xrf.model.file == file ){ // we're already loaded
|
||||
xrf.eval( url, xrf.model, flags ) // and eval local URI XR fragments
|
||||
xrf.navigator.updateHash(hash)
|
||||
return resolve(xrf.model)
|
||||
}
|
||||
|
||||
|
@ -993,8 +998,10 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
// spec: 2. execute predefined view(s) from URL (https://xrfragment.org/#predefined_view)
|
||||
xrf.eval( url, model ) // and eval URI XR fragments
|
||||
xrf.add( model.scene )
|
||||
if( !hash.match(/pos=/) )
|
||||
if( !hash.match(/pos=/) ){
|
||||
xrf.eval( '#pos=0,0,0' ) // set default position if not specified
|
||||
}
|
||||
xrf.navigator.updateHash(hash)
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
|
@ -1026,13 +1033,181 @@ xrf.navigator.pushState = (file,hash) => {
|
|||
console.log("pushstate")
|
||||
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
|
||||
}
|
||||
xrf.frag.bg = function(v, opts){
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.bg ){
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.env && !scene.environment ){
|
||||
let env = mesh.getObjectByName(frag.env.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${frag.env.string}' as environment map`)
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.fog ){
|
||||
let v = frag.fog
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
})
|
||||
xrf.macros = {}
|
||||
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
for( let k in frag ){
|
||||
let id = mesh.name+"_"+k
|
||||
let fragment = frag[k]
|
||||
|
||||
if( k.match(/^!/) ){
|
||||
if( mesh.material) mesh.material = mesh.material.clone()
|
||||
if( mesh.isSRC || scene.isSRC ) return; // dont allow recursion for now
|
||||
|
||||
if( xrf.macros[k] ) return // already initialized
|
||||
|
||||
console.log("└ initing xrmacro: "+k)
|
||||
xrf.macros[k] = fragment
|
||||
fragment.args = fragment.string.split("|")
|
||||
|
||||
fragment.trigger = (e) => {
|
||||
xrf
|
||||
.emit('macro',{click:true,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => {
|
||||
rrFrag = fragment.args[ xrf.roundrobin( fragment,model) ]
|
||||
console.log("└ xrmacro: "+rrFrag)
|
||||
if( xrf.macros[ rrFrag ] ){
|
||||
xrf.macros[ rrFrag ].trigger()
|
||||
} else {
|
||||
if( rrFrag[0] == '#' ) xrf.navigator.updateHash(rrFrag)
|
||||
else xrf.eval(rrFrag,null,0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let selected = (state) => () => {
|
||||
if( mesh.selected == state ) return // nothing changed
|
||||
if( mesh.material ){
|
||||
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
|
||||
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
|
||||
}
|
||||
// update mouse cursor
|
||||
if( !renderer.domElement.lastCursor )
|
||||
renderer.domElement.lastCursor = renderer.domElement.style.cursor
|
||||
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
|
||||
xrf
|
||||
.emit('macro',{selected:state,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => mesh.selected = state )
|
||||
}
|
||||
|
||||
mesh.addEventListener('click', fragment.trigger )
|
||||
mesh.addEventListener('mousemove', selected(true) )
|
||||
mesh.addEventListener('nocollide', selected(false) )
|
||||
|
||||
// lazy add mesh to interactive group (because we're inside a recursive traverse)
|
||||
setTimeout( (mesh) => {
|
||||
const world = {
|
||||
pos: new THREE.Vector3(),
|
||||
scale: new THREE.Vector3(),
|
||||
quat: new THREE.Quaternion()
|
||||
}
|
||||
mesh.getWorldPosition(world.pos)
|
||||
mesh.getWorldScale(world.scale)
|
||||
mesh.getWorldQuaternion(world.quat);
|
||||
mesh.position.copy(world.pos)
|
||||
mesh.scale.copy(world.scale)
|
||||
mesh.setRotationFromQuaternion(world.quat);
|
||||
xrf.interactive.add(mesh)
|
||||
}, 10, mesh )
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.mov && frag.q ){
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
},10, frag.mov )
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.pos && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.rot && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.scale && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
})
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.show && frag.q ){
|
||||
let show = frag.show
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( show.args ) v = show.args[ xrf.roundrobin(show,model) ]
|
||||
else v = show.int
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}, 20, v)
|
||||
}
|
||||
})
|
||||
xrf.frag.clip = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1043,26 +1218,6 @@ xrf.frag.clip = function(v, opts){
|
|||
camera.far = v.y
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
xrf.frag.env = function(v, opts){
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
let env = mesh.getObjectByName(v.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${v.string}' as environment map`)
|
||||
}
|
||||
xrf.frag.fog = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
xrf.frag.fov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
|
@ -1168,9 +1323,10 @@ xrf.frag.href = function(v, opts){
|
|||
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
|
||||
.then( () => {
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
//const flags = v.string[0] == '#' && v.string.match(/(\||#q)/) ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}` // always commit last position
|
||||
console.log(v.string)
|
||||
// always keep a trail of last positions before we navigate
|
||||
if( !v.string.match(/pos=/) ) v.string += `${v.string[0] == '#' ? '&' : '#'}${lastPos}`
|
||||
if( !document.location.hash.match(/pos=/) ) xrf.navigator.to(`#${lastPos}`,flags)
|
||||
|
||||
xrf.navigator.to(v.string,flags) // let's surf to HREF!
|
||||
})
|
||||
}
|
||||
|
@ -1217,36 +1373,11 @@ xrf.frag.href = function(v, opts){
|
|||
*
|
||||
* > capture of <a href="./example/aframe/sandbox" target="_blank">aframe/sandbox</a>
|
||||
*/
|
||||
xrf.frag.mov = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.pos = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}else{
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
|
||||
camera.position.x = v.x
|
||||
camera.position.y = v.y
|
||||
camera.position.z = v.z
|
||||
}
|
||||
xrf.frag.defaultPredefinedView = (opts) => {
|
||||
let {scene,model} = opts;
|
||||
|
@ -1295,7 +1426,7 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
let opts = {frag, model, camera: xrf.camera, scene: xrf.scene, renderer: xrf.renderer, THREE: xrf.THREE }
|
||||
if( frag[k].is( xrf.XRF.PV_EXECUTE ) && scene.XRF_PV_ORIGIN != k ){ // cyclic detection
|
||||
traverseScene(frag[k],scene) // recurse predefined views
|
||||
}else xrf.eval.fragment(k,opts)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1313,27 +1444,28 @@ xrf.frag.updatePredefinedView = (opts) => {
|
|||
})
|
||||
}
|
||||
|
||||
let pviews = []
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,xrf.model) ]
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
if( v.string ) pviews.push(v.string)
|
||||
}else if( v.is( xrf.XRF.NAVIGATOR ) ) pviews.push(`${i}=${v.string}`)
|
||||
// if this query was triggered by an src-value, lets filter it
|
||||
const isSRC = opts.embedded && opts.embedded.fragment == 'src'
|
||||
if( isSRC ){ // spec : https://xrfragment.org/#src
|
||||
console.log("filtering predefined view of src")
|
||||
console.dir(frag)
|
||||
}else{
|
||||
for ( let i in frag ) {
|
||||
let v = frag[i]
|
||||
if( v.is( xrf.XRF.PV_EXECUTE ) ){
|
||||
scene.XRF_PV_ORIGIN = v.string
|
||||
// wait for nested instances to arrive at the scene ?
|
||||
traverseScene(v,scene)
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pviews.length ) xrf.navigator.updateHash( pviews.join("&") )
|
||||
}
|
||||
|
||||
// react to url changes
|
||||
//xrf.addEventListener('updateHash', (opts) => {
|
||||
// console.log("update hash");
|
||||
// console.dir(opts)
|
||||
// let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
//})
|
||||
xrf.addEventListener('updateHash', (opts) => {
|
||||
let frag = xrf.URI.parse( opts.hash, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
xrf.frag.updatePredefinedView({frag,scene:xrf.scene})
|
||||
})
|
||||
|
||||
// clicking href url with predefined view
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
|
@ -1371,111 +1503,33 @@ xrf.frag.q = function(v, opts){
|
|||
return o
|
||||
})
|
||||
}
|
||||
xrf.frag.q.filter(scene,frag) // spec : https://xrfragment.org/#queries
|
||||
}
|
||||
|
||||
// spec: https://xrfragment.org/#src
|
||||
const instanceScene = () => {
|
||||
v.scene = new THREE.Group()
|
||||
for ( let i in v.query ) {
|
||||
let target = v.query[i]
|
||||
if( !scene.getObjectByName(i) && i != '*' ) return console.log(` └ mesh not found: ${i}`)
|
||||
if( i == '*' ){
|
||||
let cloneScene = scene.clone()
|
||||
// add interactive elements (href's e.g.) *TODO* this is called by both internal/external src's
|
||||
v.scene.add( xrf.interactive.clone() )
|
||||
cloneScene.children.forEach( (child) => v.scene.getObjectByName(child.name) ? null : v.scene.add(child) )
|
||||
target.mesh = v.scene
|
||||
}else{
|
||||
if( !v.scene.getObjectByName(i) && target.id === true ){
|
||||
console.log(` └ query-ing mesh: ${i}`)
|
||||
v.scene.add( target.mesh = scene.getObjectByName(i).clone() )
|
||||
}
|
||||
}
|
||||
if( target.id != undefined && target.mesh ){
|
||||
target.mesh.position.set(0,0,0)
|
||||
target.mesh.rotation.set(0,0,0)
|
||||
}
|
||||
}
|
||||
// hide negative selectors
|
||||
let negative = []
|
||||
v.scene.traverse( (mesh) => {
|
||||
for ( let i in v.query ) {
|
||||
if( mesh.name == i && v.query[i].id === false ) negative.push(mesh)
|
||||
}
|
||||
})
|
||||
negative.map( (mesh) => mesh.visible = false )
|
||||
}
|
||||
|
||||
xrf.frag.q.filter = function(scene,frag){
|
||||
// spec: https://xrfragment.org/#queries
|
||||
const showHide = () => {
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const doLocalInstancing = opts.embedded && opts.embedded.fragment == 'src' && opts.embedded.string[0] == '#'
|
||||
if( doLocalInstancing ) instanceScene() // spec : https://xrfragment.org/#src
|
||||
else showHide() // spec : https://xrfragment.org/#queries
|
||||
let q = frag.q.query
|
||||
scene.traverse( (mesh) => {
|
||||
for ( let i in q ) {
|
||||
let isMeshId = q[i].id != undefined
|
||||
let isMeshClass = q[i].class != undefined
|
||||
let isMeshProperty = q[i].rules != undefined && q[i].rules.length && !isMeshId && !isMeshClass
|
||||
if( q[i].root && mesh.isSRC ) continue; // ignore nested object for root-items (queryseletor '/foo' e.g.)
|
||||
if( isMeshId && i == mesh.name ) mesh.visible = q[i].id
|
||||
if( isMeshClass && i == mesh.userData.class ) mesh.visible = q[i].class
|
||||
if( isMeshProperty && mesh.userData[i] ) mesh.visible = (new xrf.Query(frag.q.string)).testProperty(i,mesh.userData[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
xrf.frag.rot = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}else{
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
}
|
||||
xrf.frag.scale = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
xrf.frag.show = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}
|
||||
|
||||
console.log(" └ setting camera rotation to "+v.string)
|
||||
camera.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
camera.updateMatrixWorld()
|
||||
}
|
||||
// *TODO* use webgl instancing
|
||||
|
||||
|
@ -1489,27 +1543,29 @@ xrf.frag.src = function(v, opts){
|
|||
let frag = xrfragment.URI.parse(v.string)
|
||||
|
||||
const localSRC = () => {
|
||||
|
||||
setTimeout( () => {
|
||||
// scale URI XR Fragments inside src-value
|
||||
let obj
|
||||
|
||||
// cherrypicking of object(s)
|
||||
if( !frag.q ){
|
||||
for( var i in frag ){
|
||||
if( scene.getObjectByName(i) ) src.add( obj = scene.getObjectByName(i).clone() )
|
||||
xrf.eval.fragment(i, Object.assign(opts,{frag, model,scene}))
|
||||
}
|
||||
if( frag.q.query ){
|
||||
let srcScene = frag.q.scene // three/xrf/q.js initializes .scene
|
||||
if( !srcScene || !srcScene.visible ) return
|
||||
console.log(" └ inserting "+i+" (srcScene)")
|
||||
srcScene.position.set(0,0,0)
|
||||
srcScene.rotation.set(0,0,0)
|
||||
srcScene.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ;//delete m.userData.src // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true})
|
||||
})
|
||||
if( srcScene.visible ) src.add( srcScene )
|
||||
}
|
||||
xrf.frag.src.scale( src, opts )
|
||||
},10)
|
||||
if( src.children.length == 1 ) obj.position.set(0,0,0);
|
||||
}
|
||||
|
||||
// filtering of objects using query
|
||||
if( frag.q ){
|
||||
src = scene.clone();
|
||||
src.isSRC = true;
|
||||
xrf.frag.q.filter(src,frag)
|
||||
}
|
||||
src.traverse( (m) => {
|
||||
m.isSRC = true
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
xrf.eval.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
xrf.frag.src.scale( src, opts )
|
||||
}
|
||||
|
||||
const externalSRC = () => {
|
||||
|
@ -1525,8 +1581,8 @@ xrf.frag.src = function(v, opts){
|
|||
.catch( console.error )
|
||||
}
|
||||
|
||||
if( v.string[0] == "#" ) localSRC() // current file
|
||||
else externalSRC() // external file
|
||||
if( v.string[0] == "#" ) setTimeout( localSRC, 10 ) // current file
|
||||
else externalSRC() // external file
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
|
|
|
@ -94,7 +94,7 @@ value: draft-XRFRAGMENTS-leonvankammen-00
|
|||
.# Abstract
|
||||
|
||||
This draft offers a specification for 4D URLs & navigation, to link 3D scenes and text together with- or without a network-connection.<br>
|
||||
The specification promotes spatial addressibility, sharing, navigation, query-ing and tagging interactive (text)objects across for (XR) Browsers.<br>
|
||||
The specification promotes spatial addressibility, sharing, navigation, query-ing and annotating interactive (text)objects across for (XR) Browsers.<br>
|
||||
XR Fragments allows us to enrich existing dataformats, by recursive use of existing proven technologies like [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment) and BibTags notation.<br>
|
||||
|
||||
> Almost every idea in this document is demonstrated at [https://xrfragment.org](https://xrfragment.org)
|
||||
|
@ -109,7 +109,7 @@ Their lowest common denominator is: (co)authoring using plain text.<br>
|
|||
XR Fragments allows us to enrich/connect existing dataformats, by recursive use of existing technologies:<br>
|
||||
|
||||
1. addressibility and navigation of 3D scenes/objects: [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment) + src/href spatial metadata
|
||||
1. Interlinking text/3D by deriving a Word Graph (XRWG) from the scene (and augmenting text with [bibs](https://github.com/coderofsalvation/tagbibs) / [BibTags](https://en.wikipedia.org/wiki/BibTeX) appendices (see [visual-meta](https://visual-meta.info) e.g.)
|
||||
1. Interlinking text/& 3D by collapsing space into a Word Graph (XRWG) (and augmenting text with [bibs](https://github.com/coderofsalvation/tagbibs) / [BibTags](https://en.wikipedia.org/wiki/BibTeX) appendices (see [visual-meta](https://visual-meta.info) e.g.)
|
||||
|
||||
> NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible
|
||||
|
||||
|
@ -131,6 +131,23 @@ XR Fragments itself is HTML-agnostic, though pseudo-XR Fragment browsers **can**
|
|||
|
||||
See appendix below in case certain terms are not clear.
|
||||
|
||||
## XR Fragment URI Grammar
|
||||
|
||||
```
|
||||
reserved = gen-delims / sub-delims
|
||||
gen-delims = "#" / "&"
|
||||
sub-delims = "," / "="
|
||||
```
|
||||
|
||||
> Example: `://foo.com/my3d.gltf#pos=1,0,0&prio=-5&t=0,100`
|
||||
|
||||
| Demo | Explanation |
|
||||
|-------------------------------|---------------------------------|
|
||||
| `pos=1,2,3` | vector/coordinate argument e.g. |
|
||||
| `pos=1,2,3&rot=0,90,0&q=.foo` | combinators |
|
||||
|
||||
|
||||
|
||||
# List of URI Fragments
|
||||
|
||||
| fragment | type | example | info |
|
||||
|
@ -149,7 +166,7 @@ See appendix below in case certain terms are not clear.
|
|||
| `name` | string | `"name": "cube"` | available in all 3D fileformats & scenes |
|
||||
| `class` | string | `"class": "cubes geo"` | available through custom property in 3D fileformats |
|
||||
| `href` | string | `"href": "b.gltf"` | available through custom property in 3D fileformats |
|
||||
| `src` | string | `"src": "#q=cube"` | available through custom property in 3D fileformats |
|
||||
| `src` | string | `"src": "#cube"` | available through custom property in 3D fileformats |
|
||||
|
||||
Popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `.json` (THREE.js), `.dae` and so on.
|
||||
|
||||
|
@ -191,13 +208,13 @@ Here's an ascii representation of a 3D scene-graph with 3D objects `◻` which e
|
|||
| │ └ src: painting.png | | ├─ ◻ bass |
|
||||
| │ | | └─ ◻ tuna |
|
||||
| ├── ◻ aquariumcube | | |
|
||||
| │ └ src: ://rescue.com/fish.gltf#q=bass%20tuna | +-------------------------+
|
||||
| │ └ src: ://rescue.com/fish.gltf#bass%20tuna | +-------------------------+
|
||||
| │ |
|
||||
| ├── ◻ bedroom |
|
||||
| │ └ src: #q=canvas |
|
||||
| │ └ src: #canvas |
|
||||
| │ |
|
||||
| └── ◻ livingroom |
|
||||
| └ src: #q=canvas |
|
||||
| └ src: #canvas |
|
||||
| |
|
||||
+--------------------------------------------------------+
|
||||
```
|
||||
|
@ -206,26 +223,21 @@ An XR Fragment-compatible browser viewing this scene, lazy-loads and projects `p
|
|||
Also, after lazy-loading `ocean.com/aquarium.gltf`, only the queried objects `bass` and `tuna` will be instanced inside `aquariumcube`.<br>
|
||||
Resizing will be happen accordingly to its placeholder object `aquariumcube`, see chapter Scaling.<br>
|
||||
|
||||
> Instead of cherrypicking objects with `#bass&tuna` thru `src`, queries can be used to import the whole scene (and filter out certain objects). See next chapter below.
|
||||
|
||||
# XR Fragment queries
|
||||
|
||||
Include, exclude, hide/shows objects using space-separated strings:
|
||||
|
||||
| example | outcome |
|
||||
|----------------------------------|-----------------------------------------------------------------------------|
|
||||
| `#q=cube` | show only object named `cube` (if any) |
|
||||
| `#q=cube -ball_inside_cube` | show only object named `cube` but not child named `ball_inside_cube` |
|
||||
| `#q=* -sky` | show everything except object named `sky` |
|
||||
| `#q=-.language .english` | hide everything with class `language`, then show all class `english` objects|
|
||||
| `#q=cube&rot=0,90,0` | show only object `cube` and rotate the view |
|
||||
| `#q=price:>2 price:<5` | show only objects with custom property `price` with value between 2 and 5 |
|
||||
| example | outcome |
|
||||
|----------------------------------|------------------------------------------------------------------------------------|
|
||||
| `#q=-sky` | show everything except object named `sky` |
|
||||
| `#q=-.language .english` | hide everything with class `language`, but show all class `english` objects |
|
||||
| `#q=price:>2 price:<5` | of all objects with property `price`, show only objects with value between 2 and 5 |
|
||||
|
||||
It's simple but powerful syntax which allows <b>css</b>-like class/id-selectors with a searchengine prompt-style feeling:
|
||||
|
||||
1. queries are showing/hiding objects **only** when defined as `src` value (prevents sharing of scene-tampered URL's).
|
||||
1. queries are highlighting objects when defined in the top-Level (browser) URL (bar).
|
||||
1. search words like `cube` and `foo` in `#q=cube foo` are matched against 3D object names or custom metadata-key(values)
|
||||
1. search words like `cube` and `foo` in `#q=cube foo` are matched against tags (BibTeX) inside plaintext `src` values like `@cube{redcube, ...` e.g.
|
||||
1. `#` equals `#q=*`
|
||||
1. queries are a way to traverse a scene, and filter objects based on their class- or property-values.
|
||||
1. words starting with `.` like `.german` match class-metadata of 3D objects like `"class":"german"`
|
||||
1. words starting with `.` like `.german` match class-metadata of (BibTeX) tags in XR Text objects like `@german{KarlHeinz, ...` e.g.
|
||||
|
||||
|
@ -237,7 +249,6 @@ It's simple but powerful syntax which allows <b>css</b>-like class/id-selectors
|
|||
|
||||
| operator | info |
|
||||
|----------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `*` | select all objects (only useful in `src` custom property) |
|
||||
| `-` | removes/hides object(s) |
|
||||
| `:` | indicates an object-embedded custom property key/value |
|
||||
| `.` | alias for `"class" :".foo"` equals `class:foo` |
|
||||
|
@ -272,33 +283,44 @@ Here's how to write a query parser:
|
|||
|
||||
> An example query-parser (which compiles to many languages) can be [found here](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx)
|
||||
|
||||
## XR Fragment URI Grammar
|
||||
# Embedding local/remote content (instancing)
|
||||
|
||||
```
|
||||
reserved = gen-delims / sub-delims
|
||||
gen-delims = "#" / "&"
|
||||
sub-delims = "," / "="
|
||||
```
|
||||
`src` is the 3D version of the <a target="_blank" href="https://www.w3.org/html/wiki/Elements/iframe">iframe</a>.<br>
|
||||
It instances content (in objects) in the current scene/asset.
|
||||
|
||||
> Example: `://foo.com/my3d.gltf#pos=1,0,0&prio=-5&t=0,100`
|
||||
| fragment | type | example value |
|
||||
|----------|------|---------------|
|
||||
|`src`| string (uri or [[predefined view|predefined_view]] or [[query|queries]]) | `#cube`<br>`#q=-ball_inside_cube`<br>`#q=-/sky -rain`<br>`#q=-.language .english`<br>`#q=price:>2 price:<5`<br>`https://linux.org/penguin.png`<br>`https://linux.world/distrowatch.gltf#t=1,100`<br>`linuxapp://conference/nixworkshop/apply.gltf#q=flyer`<br>`androidapp://page1?tutorial#pos=0,0,1&t1,100`|
|
||||
|
||||
|
||||
1. local/remote content is instanced by the `src` (query) value (and attaches it to the placeholder mesh containing the `src` property)
|
||||
1. <b>local</b> `src` values (URL **starting** with `#`, like `#cube&foo`) means **only** the mentioned objectnames will be copied to the instanced scene (from the current scene) while preserving their names (to support recursive selectors). [[(example code)|https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js]]
|
||||
1. <b>local</b> `src` values indicating a query (`#q=`), means that all included objects (from the current scene) will be copied to the instanced scene (before applying the query) while preserving their names (to support recursive selectors). [[(example code)|https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js]]
|
||||
1. the instanced scene (from a `src` value) should be <b>scaled accordingly</b> to its placeholder object or <b>scaled relatively</b> based on the scale-property (of a geometry-less placeholder, an 'empty'-object in blender e.g.). For more info see Chapter Scaling.
|
||||
1. <b>external</b> `src` (file) values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are:
|
||||
1. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0`
|
||||
|
||||
* `model/gltf+json`
|
||||
* `image/png`
|
||||
* `image/jpg`
|
||||
* `text/plain;charset=utf-8;bib=^@`
|
||||
|
||||
## Scaling instanced content
|
||||
|
||||
| Demo | Explanation |
|
||||
|-------------------------------|---------------------------------|
|
||||
| `pos=1,2,3` | vector/coordinate argument e.g. |
|
||||
| `pos=1,2,3&rot=0,90,0&q=.foo` | combinators |
|
||||
|
||||
|
||||
# Text in XR (tagging,linking to spatial objects)
|
||||
|
||||
How does XR Fragments interlink text with objects?
|
||||
|
||||
> The XR Fragments does this by extracting a **Word Graph** (the **XRWG**) from the current scene, facilitated by Bib(s)Tex.
|
||||
> The XR Fragments does this by collapsing space into a **Word Graph** (the **XRWG**), augmented by Bib(s)Tex.
|
||||
|
||||
The (`class`)names end up in the Word Graph (XRWG), but what about text (**inside** an article e.g.)? <br>
|
||||
Ideally metadata must come **with** that text, but not **obfuscate** the text, or **spawning another request** to fetch it.<br>
|
||||
This is done by detecting Bib(s)Tex, without introducing a new language or fileformat<br>
|
||||
Instead of just throwing together all kinds media types into one experience (games), what about the intrinsic connections between them?<br>
|
||||
Why is HTML adopted less in games outside the browser?
|
||||
Through the lens of game-making, ideally metadata must come **with** that text, but not **obfuscate** the text, or **spawning another request** to fetch it.<br>
|
||||
XR Fragments does this by detecting Bib(s)Tex, without introducing a new language or fileformat<br>
|
||||
|
||||
> Why Bib(s)Tex? Because its seems to be the lowest common denominator for a human-curate-able XRWG (extendable by speech/scanner/writing/typing e.g, see [further motivation here](https://github.com/coderofsalvation/hashtagbibs#bibs--bibtex-combo-lowest-common-denominator-for-linking-data))
|
||||
> Why Bib(s)Tex? Because its seems to be the lowest common denominator for an human-curated XRWG (extendable by speech/scanner/writing/typing e.g, see [further motivation here](https://github.com/coderofsalvation/hashtagbibs#bibs--bibtex-combo-lowest-common-denominator-for-linking-data))
|
||||
|
||||
Hence:
|
||||
|
||||
|
@ -394,6 +416,26 @@ Some pointers for good UX (but not necessary to be XR Fragment compatible):
|
|||
|
||||
> The simplicity of appending metadata (and leveling the metadata-playfield between humans and machines) is also demonstrated by [visual-meta](https://visual-meta.info) in greater detail.
|
||||
|
||||
Fictional chat:
|
||||
|
||||
```
|
||||
<John> Hey what about this: https://my.com/station.gltf#pos=0,0,1&rot=90,2,0&t=500,1000
|
||||
<Sarah> I'm checking it right now
|
||||
<Sarah> I don't see everything..where's our text from yesterday?
|
||||
<John> Ah wait, that's tagged with class 'draft' (and hidden)..hold on, try this:
|
||||
<John> https://my.com/station.gltf#.draft&pos=0,0,1&rot=90,2,0&t=500,1000
|
||||
<Sarah> how about we link the draft to the upcoming YELLO-event?
|
||||
<John> ok I'm adding #draft@YELLO
|
||||
<Sarah> Yesterday I also came up with other usefull assocations between other texts in the scene:
|
||||
#event#YELLO
|
||||
#2025@YELLO
|
||||
<John> thanks, added.
|
||||
<Sarah> Btw. I stumbled upon this spatial book which references station.gltf in some chapters:
|
||||
<Sarah> https://thecommunity.org/forum/foo/mytrainstory.txt
|
||||
<John> interesting, I'm importing mytrainstory.txt into station.gltf
|
||||
<John> ah yes, chapter three points to trainterminal_2A, cool
|
||||
```
|
||||
|
||||
## Default Data URI mimetype
|
||||
|
||||
The `src`-values work as expected (respecting mime-types), however:
|
||||
|
@ -454,9 +496,9 @@ The XR Fragment-compatible browser can let the enduser access visual-meta(data)-
|
|||
|
||||
> additional tagging using [bibs](https://github.com/coderofsalvation/hashtagbibs): to tag spatial object `note_canvas` with 'todo', the enduser can type or speak `#note_canvas@todo`
|
||||
|
||||
## XR Text example parser
|
||||
## XR Text example parser
|
||||
|
||||
Here's an example XR Text (de)multiplexer in javascript, which supports inline bibs & bibtex:
|
||||
To prime the XRWG with text from plain text `src`-values, here's an example XR Text (de)multiplexer in javascript (which supports inline bibs & bibtex):
|
||||
|
||||
```
|
||||
xrtext = {
|
||||
|
@ -519,7 +561,7 @@ xrtext = {
|
|||
}
|
||||
```
|
||||
|
||||
The above functions (de)multiplexe text/metadata, expands bibs, (de)serialize bibtex (and all fits more or less on one A4 paper)
|
||||
The above functions (de)multiplexe text/metadata, expands bibs, (de)serialize bibtex and vice versa
|
||||
|
||||
> above can be used as a startingpoint for LLVM's to translate/steelman to a more formal form/language.
|
||||
|
||||
|
@ -564,16 +606,6 @@ here are some hashtagbibs followed by bibtex:
|
|||
|
||||
> when an XR browser updates the human text, a quick scan for nonmatching tags (`@book{nonmatchingbook` e.g.) should be performed and prompt the enduser for deleting them.
|
||||
|
||||
# HYPER copy/paste
|
||||
|
||||
The previous example, offers something exciting compared to simple copy/paste of 3D objects or text.
|
||||
XR Text according to the XR Fragment spec, allows HYPER-copy/paste: time, space and text interlinked.
|
||||
Therefore, the enduser in an XR Fragment-compatible browser can copy/paste/share data in these ways:
|
||||
|
||||
1. time/space: 3D object (current animation-loop)
|
||||
1. text: TeXt object (including BibTeX/visual-meta if any)
|
||||
1. interlinked: Collected objects by visual-meta tag
|
||||
|
||||
# Security Considerations
|
||||
|
||||
Since XR Text contains metadata too, the user should be able to set up tagging-rules, so the copy-paste feature can :
|
||||
|
|
|
@ -108,7 +108,7 @@ Their lowest common denominator is: (co)authoring using plain text.<br>
|
|||
Therefore, XR Macros allows us to enrich/connect existing dataformats, by offering a polyglot notation based on existing notations:<br>
|
||||
|
||||
1. getting/setting common used 3D properties using querystring- or JSON-notation
|
||||
1. querying 3D properties using the lightweight searchengine notation used in [XR Fragments](https://xrfragment.org)
|
||||
1. targeting 3D properties using the lightweight query notation present in [XR Fragments](https://xrfragment.org)
|
||||
|
||||
> NOTE: The chapters in this document are ordered from highlevel to lowlevel (technical) as much as possible
|
||||
|
||||
|
@ -148,14 +148,14 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
|
||||
| custom property | value | trigger when |
|
||||
|-----------------|--------------------------|------------------------|
|
||||
| !clickme | day|noon|night | object clicked |
|
||||
| !cycleme | day|noon|night | object clicked |
|
||||
| day | bg=1,1,1 | roundrobin |
|
||||
| noon | bg=0.5,0.5,0.5 | roundrobin |
|
||||
| night | bg=0,0,0&foo=2 | roundrobin |
|
||||
|
||||
> when a user clicks an object with the custom properties above, it should trigger either `day` `noon` or `night` in roundrobin fashion.
|
||||
|
||||
## Usecase: click object, URI fragment and scene load
|
||||
## Usecase: click object or URI fragment, and scene load trigger
|
||||
|
||||
| custom property | value | trigger when |
|
||||
|-----------------|--------------------------|------------------------|
|
||||
|
@ -170,12 +170,47 @@ Macros also act as events, so more serious scripting languages can react to them
|
|||
|
||||
| custom property | value | trigger when |
|
||||
|-----------------|--------------------------|------------------------|
|
||||
| !random | day|noon|night | clicked in contextmenu |
|
||||
| !random | !day|!noon|!night | clicked in contextmenu |
|
||||
| !day | bg=1,1,1 | clicked in contextmenu |
|
||||
| !noon | bg=0.5,0.5,0.5 | clicked in contextmenu |
|
||||
| !night | bg=0,0,0&foo=2 | clicked in contextmenu |
|
||||
|
||||
> The XR Browser should offer a contextmenu with these options when more than one `!`-macro is present on an object.
|
||||
> When interacting with an object with more than one `!`-macro, the XR Browser should offer a contextmenu to execute a macro.
|
||||
|
||||
In a similar way, when **any** `!`-macro is present on the sceneroot, the XR Browser should offer a context-menu to execute those macro's.
|
||||
|
||||
## Event Bubble-flow
|
||||
|
||||
click object with (`!clickme`:`AR` or `!clickme`: `!reset` e.g.)
|
||||
```
|
||||
◻
|
||||
│
|
||||
└── does current object contain this property-key (`AR` or `!reset` e.g.)?
|
||||
└── no: is there any (root)object containing property `AR`
|
||||
└── yes: evaluate its (roundrobin) XR macro-value(s) (and exit)
|
||||
└── no: trigger URL: #AR
|
||||
```
|
||||
|
||||
click object with (`!clickme`:`#AR|#VR` e.g.)
|
||||
```
|
||||
◻
|
||||
│
|
||||
└── apply the roundrobin (rotate the options, value `#AR` becomes `#VR` upon next click)
|
||||
└── is there any object with property-key (`#AR` e.g.)?
|
||||
└── no: just update the URL to `#AR`
|
||||
└── yes: apply its value to the scene, and update the URL to `#AR`
|
||||
|
||||
click object with (`!clickme`:`!foo|!bar|!flop` e.g.)
|
||||
```
|
||||
◻
|
||||
│
|
||||
└── apply the roundrobin (rotate the options, value `!foo` becomes `!bar` upon next click)
|
||||
└── is there any object with property-key (`!foo` e.g.)?
|
||||
└── no: do nothing
|
||||
└── yes: apply its value to the scene
|
||||
```
|
||||
|
||||
> Note that only macro's can trigger roundrobin values or contextmenu's, as well as roundrobin values never ending up in the toplevel URL.
|
||||
|
||||
# Security Considerations
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -109,13 +109,6 @@
|
|||
xrf(v,opts)
|
||||
}
|
||||
|
||||
// optional: react/extend/hook into custom fragment (for non-standard custom framework logic e.g.)
|
||||
XRF.foobar = (xrf,v,opts) => {
|
||||
let { mesh, model, camera, scene, renderer, THREE} = opts
|
||||
console.log("hello custom property 'foobar'")
|
||||
}
|
||||
|
||||
|
||||
// *TODO* lowhanging fruit: during XR fragments-query milestone, target objects using XR fragment queries
|
||||
// to provide jquery-ish interface for three.js)
|
||||
//
|
||||
|
|
7
make
7
make
|
@ -66,9 +66,10 @@ build_js(){
|
|||
cat dist/xrfragment.js > dist/xrfragment.module.js
|
||||
echo "export default xrfragment;" >> dist/xrfragment.module.js
|
||||
# add THREE
|
||||
cat dist/xrfragment.js \
|
||||
src/3rd/js/*.js \
|
||||
src/3rd/js/three/*.js \
|
||||
cat dist/xrfragment.js \
|
||||
src/3rd/js/*.js \
|
||||
src/3rd/js/three/*.js \
|
||||
src/3rd/js/three/xrmacro/*.js \
|
||||
src/3rd/js/three/xrf/*.js > dist/xrfragment.three.js
|
||||
# add THREE module
|
||||
cat dist/xrfragment.three.js > dist/xrfragment.three.module.js
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.bg ){
|
||||
console.log("└ bg "+v.x+","+v.y+","+v.z);
|
||||
if( scene.background ) delete scene.background
|
||||
scene.background = new THREE.Color( v.x, v.y, v.z )
|
||||
}
|
||||
})
|
|
@ -0,0 +1,13 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.env && !scene.environment ){
|
||||
let env = mesh.getObjectByName(frag.env.string)
|
||||
if( !env ) return console.warn("xrf.env "+v.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//scene.texture = env.material.map
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${frag.env.string}' as environment map`)
|
||||
}
|
||||
})
|
|
@ -0,0 +1,11 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.fog ){
|
||||
let v = frag.fog
|
||||
console.log("└ fog "+v.x+","+v.y);
|
||||
if( v.x == 0 && v.y == 0 ){
|
||||
if( scene.fog ) delete scene.fog
|
||||
scene.fog = null;
|
||||
}else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
|
||||
}
|
||||
})
|
|
@ -0,0 +1,70 @@
|
|||
xrf.macros = {}
|
||||
|
||||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
for( let k in frag ){
|
||||
let id = mesh.name+"_"+k
|
||||
let fragment = frag[k]
|
||||
|
||||
if( k.match(/^!/) ){
|
||||
if( mesh.material) mesh.material = mesh.material.clone()
|
||||
if( mesh.isSRC || scene.isSRC ) return; // dont allow recursion for now
|
||||
|
||||
if( xrf.macros[k] ) return // already initialized
|
||||
|
||||
console.log("└ initing xrmacro: "+k)
|
||||
xrf.macros[k] = fragment
|
||||
fragment.args = fragment.string.split("|")
|
||||
|
||||
fragment.trigger = (e) => {
|
||||
xrf
|
||||
.emit('macro',{click:true,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => {
|
||||
rrFrag = fragment.args[ xrf.roundrobin( fragment,model) ]
|
||||
console.log("└ xrmacro: "+rrFrag)
|
||||
if( xrf.macros[ rrFrag ] ){
|
||||
xrf.macros[ rrFrag ].trigger()
|
||||
} else {
|
||||
if( rrFrag[0] == '#' ) xrf.navigator.updateHash(rrFrag)
|
||||
else xrf.eval(rrFrag,null,0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let selected = (state) => () => {
|
||||
if( mesh.selected == state ) return // nothing changed
|
||||
if( mesh.material ){
|
||||
if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
|
||||
else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
|
||||
}
|
||||
// update mouse cursor
|
||||
if( !renderer.domElement.lastCursor )
|
||||
renderer.domElement.lastCursor = renderer.domElement.style.cursor
|
||||
renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
|
||||
xrf
|
||||
.emit('macro',{selected:state,mesh,xrf:frag}) // let all listeners agree
|
||||
.then( () => mesh.selected = state )
|
||||
}
|
||||
|
||||
mesh.addEventListener('click', fragment.trigger )
|
||||
mesh.addEventListener('mousemove', selected(true) )
|
||||
mesh.addEventListener('nocollide', selected(false) )
|
||||
|
||||
// lazy add mesh to interactive group (because we're inside a recursive traverse)
|
||||
setTimeout( (mesh) => {
|
||||
const world = {
|
||||
pos: new THREE.Vector3(),
|
||||
scale: new THREE.Vector3(),
|
||||
quat: new THREE.Quaternion()
|
||||
}
|
||||
mesh.getWorldPosition(world.pos)
|
||||
mesh.getWorldScale(world.scale)
|
||||
mesh.getWorldQuaternion(world.quat);
|
||||
mesh.position.copy(world.pos)
|
||||
mesh.scale.copy(world.scale)
|
||||
mesh.setRotationFromQuaternion(world.quat);
|
||||
xrf.interactive.add(mesh)
|
||||
}, 10, mesh )
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,12 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.mov && frag.q ){
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
|
||||
})
|
||||
},10, frag.mov )
|
||||
}
|
||||
})
|
|
@ -0,0 +1,14 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.pos && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
// if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
|
||||
o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
|
||||
o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
|
||||
o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
|
||||
})
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.rot && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
if( frag.q ){ // only operate on queried object(s)
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.rotation.set(
|
||||
v.x * Math.PI / 180,
|
||||
v.y * Math.PI / 180,
|
||||
v.z * Math.PI / 180
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,13 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.scale && frag.q ){
|
||||
// apply roundrobin (if any)
|
||||
if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
|
||||
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.scale.x = v.x
|
||||
o.scale.y = v.y
|
||||
o.scale.z = v.z
|
||||
})
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
xrf.addEventListener('eval', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.show && frag.q ){
|
||||
let show = frag.show
|
||||
|
||||
// apply roundrobin (if any)
|
||||
if( show.args ) v = show.args[ xrf.roundrobin(show,model) ]
|
||||
else v = show.int
|
||||
|
||||
// let wait for the queried objects (as we're inside promise which traverses the graph)
|
||||
setTimeout( (v) => {
|
||||
frag.q.getObjects().map( (o) => {
|
||||
o.visible = v.int == 1;
|
||||
})
|
||||
}, 20, v)
|
||||
}
|
||||
})
|
|
@ -380,7 +380,8 @@ xrfragment_Parser.parse = function(key,value,store) {
|
|||
if(typeof(value) == "string") {
|
||||
v.guessType(v,value);
|
||||
}
|
||||
store["_" + key] = v;
|
||||
v.noXRF = true;
|
||||
store[key] = v;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -1456,8 +1456,8 @@ class xrfragment_Parser:
|
|||
else:
|
||||
if Std.isOfType(value,str):
|
||||
v.guessType(v,value)
|
||||
key1 = ("_" + ("null" if key is None else key))
|
||||
setattr(store,(("_hx_" + key1) if ((key1 in python_Boot.keywords)) else (("_hx_" + key1) if (((((len(key1) > 2) and ((ord(key1[0]) == 95))) and ((ord(key1[1]) == 95))) and ((ord(key1[(len(key1) - 1)]) != 95)))) else key1)),v)
|
||||
v.noXRF = True
|
||||
setattr(store,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),v)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -1611,8 +1611,8 @@ class xrfragment_Query:
|
|||
fails = 0
|
||||
qualify = 0
|
||||
def _hx_local_2(expr):
|
||||
nonlocal conds
|
||||
nonlocal fails
|
||||
nonlocal conds
|
||||
conds = (conds + 1)
|
||||
fails = (fails + (0 if expr else 1))
|
||||
return expr
|
||||
|
@ -1701,12 +1701,13 @@ class xrfragment_URI:
|
|||
|
||||
class xrfragment_XRF:
|
||||
_hx_class_name = "xrfragment.XRF"
|
||||
__slots__ = ("fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query")
|
||||
_hx_fields = ["fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query"]
|
||||
__slots__ = ("fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query", "noXRF")
|
||||
_hx_fields = ["fragment", "flags", "x", "y", "z", "color", "string", "int", "float", "args", "query", "noXRF"]
|
||||
_hx_methods = ["is", "validate", "guessType"]
|
||||
_hx_statics = ["ASSET", "PROP_BIND", "QUERY_OPERATOR", "PROMPT", "ROUNDROBIN", "NAVIGATOR", "METADATA", "PV_OVERRIDE", "PV_EXECUTE", "T_COLOR", "T_INT", "T_FLOAT", "T_VECTOR2", "T_VECTOR3", "T_URL", "T_PREDEFINED_VIEW", "T_STRING", "T_STRING_OBJ", "T_STRING_OBJ_PROP", "isColor", "isInt", "isFloat", "isVector", "isUrl", "isUrlOrPretypedView", "isString", "set", "unset"]
|
||||
|
||||
def __init__(self,_fragment,_flags):
|
||||
self.noXRF = None
|
||||
self.query = None
|
||||
self.args = None
|
||||
self.float = None
|
||||
|
|
Loading…
Reference in New Issue