first attempt
This commit is contained in:
parent
8b4fde3571
commit
e42fda84b4
|
@ -30,7 +30,7 @@
|
|||
<textarea style="display:none"></textarea>
|
||||
<canvas id="qrcode" style="display:none" width="300" height="300"></canvas>
|
||||
|
||||
<a-scene light="defaultLightsEnabled: false">
|
||||
<a-scene renderer="colorManagement: true; highRefreshRate:true" light="defaultLightsEnabled: false">
|
||||
<a-entity id="player" wasd-controls look-controls>
|
||||
<a-entity camera="fov:90" position="0 1.6 0" id="camera"></a-entity>
|
||||
<a-entity id="left-hand" laser-controls="hand: left" raycaster="objects:.ray" blink-controls="cameraRig:#player; teleportOrigin: #camera; collisionEntities: #floor">
|
||||
|
@ -57,34 +57,23 @@
|
|||
window.embed = embed
|
||||
window.notify('loading '+document.location.search.substr(1))
|
||||
|
||||
// reroute console messages to snackbar notifications
|
||||
console.log = ( (log) => function(str){
|
||||
if( str.match(/URL:/) ) str += "<br><br>(use back-button to go back)"
|
||||
if( String(str).match(/:.*#/) ) window.notify(str)
|
||||
log(str)
|
||||
})(console.log)
|
||||
|
||||
|
||||
|
||||
let XRF = window.AFRAME.XRF
|
||||
setupConsole( $('textarea') )
|
||||
setupUrlBar( $('input#uri'), XRF )
|
||||
|
||||
// example listener, to prompt surfing to external asset
|
||||
XRF.addEventListener('href',(e) => {
|
||||
if( e.click ){
|
||||
const { mesh, model, camera, scene, renderer, THREE} = e.XRF
|
||||
const url = e.xrf.string
|
||||
const isExtern = url[0] != '#'
|
||||
const notIsHome = url != $('#home').getAttribute("xrf")
|
||||
const promise = e.promise() // promisify event
|
||||
document.querySelector('#model').setAttribute('href',url.replace(/.*\?/,'') )
|
||||
if( !renderer.xr.isPresenting ){
|
||||
if( isExtern && notIsHome ){
|
||||
if( !confirm("teleport to "+url+" ?") ) return promise.reject('teleport rejected')
|
||||
}
|
||||
}
|
||||
promise.resolve()
|
||||
}
|
||||
})
|
||||
// on localhost enable debugging mode for developer convenience
|
||||
let loc = document.location
|
||||
if( loc.host.match(/^localhost/) ){
|
||||
$('a-scene').setAttribute('stats')
|
||||
XRF.debug = 1
|
||||
}
|
||||
|
||||
// add screenshot component with camera to capture bigger size equirects
|
||||
// document.querySelector('a-scene').components.screenshot.capture('perspective')
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -267,7 +267,7 @@ export function notify(scope){
|
|||
if( str.match(/error/g) ) opts.status = "danger"
|
||||
if( str.match(/warning/g) ) opts.status = "warning"
|
||||
}
|
||||
opts = Object.assign({ message: str , status, timeout:2000 },opts)
|
||||
opts = Object.assign({ message: str , status, timeout:4000 },opts)
|
||||
SnackBar( opts )
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,18 +12,19 @@ XRWG.cleankey = (word) => String(word).replace(/[^0-9\.a-zA-Z_]/g,'')
|
|||
XRWG.get = (v,k) => XRWG.find( (x) => x[ k || 'word'] == v )
|
||||
|
||||
XRWG.match = (str,types,level) => {
|
||||
level = level || 1000
|
||||
if( XRWG.length == 0 ) XRWG.generate(xrf)
|
||||
level = level == undefined ? 1000 : level
|
||||
types = types || []
|
||||
let res = XRWG.filter( (n) => {
|
||||
types.map( (type) => n[type] ? n = false : false )
|
||||
return n
|
||||
})
|
||||
str = str.toLowerCase()
|
||||
if( level <10 ) res = res.filter( (n) => n.key == str )
|
||||
if( level <20 ) res = res.filter( (n) => n.word == str || n.key == str )
|
||||
if( level <30 ) res = res.filter( (n) => n.word.match(str) || n.key == str )
|
||||
if( level <40 ) res = res.filter( (n) => n.word.match(str) || n.key == str || String(n.value||'').match(str) )
|
||||
if( level <1001 ) res = res.filter( (n) => n.word.match(str) != null || n.key.match(str) != null || String(n.value||'').match(str) != null)
|
||||
if( level <10 ) res = res.filter( (n) => n.key == str )
|
||||
if( level >=10 ) res = res.filter( (n) => n.word == str || n.key == str )
|
||||
if( level >30 ) res = res.filter( (n) => n.word.match(str) || n.key == str )
|
||||
if( level >40 ) res = res.filter( (n) => n.word.match(str) || n.key == str || String(n.value||'').match(str) )
|
||||
if( level >999 ) res = res.filter( (n) => n.word.match(str) != null || n.key.match(str) != null || String(n.value||'').match(str) != null)
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,7 @@ XRWG.generate = (opts) => {
|
|||
node = { word: XRWG.cleankey(key), key, nodes:[spatialNode] }
|
||||
if( spatialNode.userData[key] ) node.value = spatialNode.userData[key]
|
||||
node[type] = true
|
||||
xrf.emit('XRWG',node)
|
||||
xrf.emit('XRWGnode',node)
|
||||
XRWG.push( node )
|
||||
}
|
||||
}
|
||||
|
@ -64,4 +65,5 @@ XRWG.generate = (opts) => {
|
|||
// sort by n
|
||||
XRWG.sort( (a,b) => a.nodes.length - b.nodes.length )
|
||||
XRWG = XRWG.reverse() // the cleankey/get functions e.g. will persist
|
||||
xrf.emit('XRWG',XRWG)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@ window.AFRAME.registerComponent('xrf', {
|
|||
},
|
||||
init: function () {
|
||||
if( !AFRAME.XRF ){
|
||||
|
||||
// start with black
|
||||
document.querySelector('[camera]').setAttribute('xrf-fade','')
|
||||
AFRAME.fade = document.querySelector('[camera]').components['xrf-fade']
|
||||
|
||||
document.querySelector('a-scene').addEventListener('loaded', () => {
|
||||
|
||||
// enable XR fragments
|
||||
|
@ -12,7 +17,6 @@ window.AFRAME.registerComponent('xrf', {
|
|||
camera: aScene.camera,
|
||||
scene: aScene.object3D,
|
||||
renderer: aScene.renderer,
|
||||
debug: true,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader
|
||||
|
@ -20,6 +24,28 @@ window.AFRAME.registerComponent('xrf', {
|
|||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
||||
xrf.addEventListener('navigateLoaded', () => setTimeout( () => AFRAME.fade.out(),500) )
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click){
|
||||
console.dir(opts)
|
||||
let p = opts.promise()
|
||||
if( opts.xrf.string[0] == '#' ){ // local teleport // local teleport // local teleport // local teleport
|
||||
let fastFadeMs = 200
|
||||
AFRAME.fade.in(fastFadeMs)
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
AFRAME.fade.out(fastFadeMs)
|
||||
}, fastFadeMs)
|
||||
}else{
|
||||
AFRAME.fade.in()
|
||||
setTimeout( () => {
|
||||
p.resolve()
|
||||
setTimeout( () => AFRAME.fade.out(), 1000 ) // allow one second to load textures e.g.
|
||||
}, AFRAME.fade.data.fadetime )
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// in order to set the rotation programmatically
|
||||
// we need to disable look-controls
|
||||
xrf.rot = (xrf,v,opts) => {
|
||||
|
@ -40,9 +66,11 @@ window.AFRAME.registerComponent('xrf', {
|
|||
el.setAttribute("xrf-get",mesh.name ) // turn into AFRAME entity
|
||||
el.setAttribute("class","ray") // expose to raycaster
|
||||
el.setAttribute("pressable", '') // detect hand-controller click
|
||||
// add click
|
||||
// respond to cursor via laser-controls (https://aframe.io/docs/1.4.0/components/laser-controls.html)
|
||||
el.addEventListener("click", clickHandler )
|
||||
//el.addEventListener("pressedstarted", clickHandler )
|
||||
el.addEventListener("mouseenter", mesh.userData.XRF.href.selected(true) )
|
||||
el.addEventListener("mouseleave", mesh.userData.XRF.href.selected(false) )
|
||||
el.addEventListener("pressedstarted", clickHandler )
|
||||
$('a-scene').appendChild(el)
|
||||
}
|
||||
xrf.addEventListener('interactionReady', AFRAME.XRF.clickableMeshToEntity )
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
AFRAME.registerComponent('xrf-fade', {
|
||||
schema:{
|
||||
fadetime:{type:"number", default: 1000},
|
||||
color:{type:"color", default:"black"},
|
||||
opacity:{type:"float",default:1.0}
|
||||
},
|
||||
init: function(){
|
||||
let fb = this.fb = document.createElement("a-box")
|
||||
fb.setAttribute("scale", "1 1 1")
|
||||
fb.setAttribute("material", `color: ${this.data.color}; transparent: true; side: back; shader: flat; opacity:1`)
|
||||
this.el.appendChild(fb)
|
||||
},
|
||||
out: function(fadetime){
|
||||
if( fadetime != undefined ) this.data.fadetime = fadetime
|
||||
if( this.data.opacity == 0 ) return
|
||||
this.data.opacity = 0.0
|
||||
this.fb.setAttribute("animation", `property: components.material.material.opacity; dur: ${this.data.fadetime}; from: 1; to: ${this.data.opacity}`)
|
||||
setTimeout( () => this.fb.object3D.visible = false, this.data.fadetime )
|
||||
},
|
||||
"in": function(fadetime){
|
||||
if( fadetime != undefined ) this.data.fadetime = fadetime
|
||||
if( this.data.opacity == 1 ) return
|
||||
this.data.opacity = 1.0
|
||||
this.fb.object3D.visible = true
|
||||
this.fb.setAttribute("animation", `property: components.material.material.opacity; dur: ${this.data.fadetime}; from: 0; to: ${this.data.opacity}`)
|
||||
}
|
||||
});
|
|
@ -4,7 +4,8 @@
|
|||
var xrf = {}
|
||||
|
||||
xrf.init = function(opts){
|
||||
opts = opts || {}
|
||||
opts = opts || {}
|
||||
xrf.debug = parseInt( ( document.location.hash.match(/debug=([0-9])/) || [0,'0'] )[1] )
|
||||
xrf.Parser.debug = xrf.debug
|
||||
xrf.detectCameraRig(opts)
|
||||
for ( let i in opts ) xrf[i] = opts[i]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* (promise-able) EVENTS
|
||||
* (promise-able) EVENTS (optionally continue after listeners are finished using .then)
|
||||
*
|
||||
* example:
|
||||
*
|
||||
|
@ -24,14 +24,23 @@ xrf.addEventListener = function(eventName, callback, scene) {
|
|||
// add the callback to the listeners array for this event name
|
||||
this._listeners[eventName].push(callback);
|
||||
return () => {
|
||||
console.log("size = "+this._listeners[eventName].length)
|
||||
this._listeners[eventName] = this._listeners[eventName].filter( (c) => c != callback )
|
||||
console.log("size = "+this._listeners[eventName].length)
|
||||
}
|
||||
};
|
||||
|
||||
xrf.emit = function(eventName, data){
|
||||
if( typeof data != 'object' ) throw 'emit() requires passing objects'
|
||||
if( xrf.debug && ( eventName != "render" || xrf.debug == eventName ) ){
|
||||
let label = String(`xrf.emit('${eventName}')`).padEnd(35," ");
|
||||
label += data.mesh && data.mesh.name ? '#'+data.mesh.name : ''
|
||||
console.groupCollapsed(label)
|
||||
console.info(data)
|
||||
console.groupEnd(label)
|
||||
if( xrf.debug > 1 ) debugger
|
||||
}
|
||||
// forward to THREEjs eventbus if any
|
||||
if( data.scene ) data.scene.dispatchEvent( eventName, data )
|
||||
if( data.mesh ) data.mesh.dispatchEvent( eventName, data )
|
||||
return xrf.emit.promise(eventName,data)
|
||||
}
|
||||
|
||||
|
@ -46,15 +55,21 @@ xrf.emit.normal = function(eventName, data) {
|
|||
};
|
||||
|
||||
xrf.emit.promise = function(e, opts){
|
||||
opts.XRF = xrf // always pass root XRF obj
|
||||
return new Promise( (resolve, reject) => {
|
||||
opts.promise = () => {
|
||||
opts.promise.halted = true
|
||||
return { resolve, reject }
|
||||
opts.promises = opts.promises || []
|
||||
opts.promises.push(0)
|
||||
return {
|
||||
resolve: ((index) => () => {
|
||||
opts.promises[index] = 1
|
||||
let succesful = opts.promises.reduce( (a,b) => a+b )
|
||||
if( succesful == opts.promises.length ) resolve(opts)
|
||||
})(opts.promises.length-1),
|
||||
reject: console.error
|
||||
}
|
||||
}
|
||||
xrf.emit.normal(e, opts)
|
||||
delete opts.XRF
|
||||
if( !opts.promise.halted ) resolve()
|
||||
if( !opts.promises ) resolve(opts)
|
||||
delete opts.promise
|
||||
})
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -26,7 +26,7 @@ pub.mesh = (mesh,model) => { // evaluate embedded fragments (metadata) insid
|
|||
for( let k in frag ){
|
||||
let opts = {frag, mesh, model, camera: xrf.camera, scene: model.scene, renderer: xrf.renderer, THREE: xrf.THREE, hashbus: xrf.hashbus }
|
||||
mesh.userData.XRF = frag // allow fragment impl to access XRF obj already
|
||||
xrf.emit('mesh',opts)
|
||||
xrf.emit('frag2mesh',opts)
|
||||
.then( () => pub.fragment(k,opts) )
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ pub.fragment = (k, opts ) => { // evaluate one fragment
|
|||
xrf.emit(k,opts)
|
||||
.then( () => {
|
||||
let func = xrf.frag[k] || function(){}
|
||||
if( xrf[k] ) xrf[k]( func, frag, opts)
|
||||
else func( frag, opts)
|
||||
if( typeof xrf[k] == 'function' ) xrf[k]( func, frag, opts)
|
||||
else func( frag, opts)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ xrf.patchRenderer = function(opts){
|
|||
// update clock
|
||||
let time = xrf.clock.getDelta()
|
||||
// allow entities to do stuff during render (onBeforeRender and onAfterRender don't always fire)
|
||||
xrf.emit('render',{scene,camera,time}) // allow fragments to do something at renderframe
|
||||
xrf.emit('render',{scene,camera,time,render}) // allow fragments to do something at renderframe
|
||||
render(scene,camera)
|
||||
})(renderer.render.bind(renderer))
|
||||
|
||||
|
@ -53,7 +53,6 @@ xrf.parseModel = function(model,url){
|
|||
model.file = file
|
||||
// eval embedded XR fragments
|
||||
model.scene.traverse( (mesh) => {
|
||||
mesh.renderOrder = 2 // render after stencil buffers
|
||||
xrf.hashbus.pub.mesh(mesh,model)
|
||||
})
|
||||
model.animations.map( (a) => console.log("anim: "+a.name) )
|
||||
|
@ -83,10 +82,7 @@ xrf.reset = () => {
|
|||
xrf.layers = 0
|
||||
xrf.emit('reset',{})
|
||||
// remove mixers
|
||||
xrf.mixers.map( (m) => {
|
||||
m.stop()
|
||||
delete m
|
||||
})
|
||||
xrf.mixers.map( (m) => m.stop())
|
||||
xrf.mixers = []
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
if( !url ) throw 'xrf.navigator.to(..) no url given'
|
||||
|
||||
let hashbus = xrf.hashbus
|
||||
xrf.emit('navigate', {url,loader,data})
|
||||
|
||||
return new Promise( (resolve,reject) => {
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
|
@ -43,6 +44,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
|
|||
},2000)
|
||||
xrf.add( model.scene )
|
||||
xrf.navigator.updateHash(hash)
|
||||
xrf.emit('navigateLoaded',{url,model})
|
||||
resolve(model)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
xrf.portalNonEuclidian = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
let toFrag = xrf.URI.parse( v.string )
|
||||
|
||||
// turn plane into stencilplane
|
||||
mesh.material = new THREE.MeshPhongMaterial({ color: 'green' });
|
||||
mesh.material.depthWrite = false;
|
||||
mesh.material.stencilWrite = true;
|
||||
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
|
||||
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
|
||||
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
|
||||
//mesh.material.side = THREE.DoubleSide (this requires flipping normals based on camerapos)
|
||||
mesh.stencilRef = xrf.portalNonEuclidian.stencilRef
|
||||
|
||||
let stencilPos = new xrf.THREE.Vector3()
|
||||
mesh.getWorldPosition(stencilPos)
|
||||
|
||||
// allow objects to flip between original and stencil position (which puts them behind stencilplane)
|
||||
const addStencilFeature = (n) => {
|
||||
n.stencil = (
|
||||
(pos,stencilPos, stencilMaterial, material ) => (enabled) => {
|
||||
if( mesh.stencilScene.active ) enabled = false // always deactive when stencil was clicked
|
||||
let sRef = enabled ? mesh.stencilRef : 0
|
||||
n.position.copy( enabled ? stencilPos : pos )
|
||||
xrf.portalNonEuclidian.selectStencil(n, sRef )
|
||||
n.traverse( (c) => xrf.portalNonEuclidian.selectStencil(c,sRef) )
|
||||
}
|
||||
)( n.position.clone(), stencilPos, n.material.clone, n.material )
|
||||
return n
|
||||
}
|
||||
|
||||
// collect related objects from XRWG to render inside stencilplane
|
||||
let objs = XRWG.match(mesh.name,0)
|
||||
if( objs.length == 0 ) return console.warn(`no objects are tagged with (portal)object name '${mesh.name}'`)
|
||||
objs = objs[0].nodes
|
||||
.filter( (n) => n.uuid != mesh.uuid && !n.stencilRef ) // filter out stencilplane
|
||||
.map(addStencilFeature)
|
||||
|
||||
// put it into a scene so we can render it separately
|
||||
mesh.stencilScene = new xrf.THREE.Scene()
|
||||
mesh.stencilScene.children = objs
|
||||
|
||||
// enable the stencil-material of the stencil objects
|
||||
mesh.onAfterRender = function(){
|
||||
mesh.stencilScene.traverse( (n) => n.stencil ? n.stencil(true) : false )
|
||||
}
|
||||
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
if( opts.click && opts.mesh.uuid == mesh.uuid){
|
||||
// reposition stencilScene objects back to their original (and disable the stencil material)
|
||||
mesh.stencilScene.traverse( (n) => n.stencil ? n.stencil(false) : false )
|
||||
mesh.stencilScene.active = true
|
||||
}else mesh.stencilScene.active = false
|
||||
})
|
||||
|
||||
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
|
||||
console.log("enabling portal for object '${mesh.name}'`")
|
||||
}
|
||||
|
||||
xrf.portalNonEuclidian.selectStencil = (n, stencilRef, material) => {
|
||||
if( n.material ){
|
||||
if( !n.stencilMaterial ){
|
||||
n.originalMaterial = n.material
|
||||
n.stencilMaterial = n.material.clone()
|
||||
n.stencilMaterial.stencilRef = stencilRef
|
||||
n.stencilMaterial.stencilWrite = stencilRef
|
||||
n.stencilMaterial.stencilFunc = xrf.THREE.EqualStencilFunc;
|
||||
}
|
||||
n.material = stencilRef > 0 ? n.stencilMaterial : n.originalMaterial
|
||||
}
|
||||
}
|
||||
|
||||
xrf.portalNonEuclidian.stencilRef = 1
|
|
@ -17,6 +17,7 @@ xrf.addEventListener('dynamicKey', (opts) => {
|
|||
|
||||
xrf.drawLineToMesh = (opts) => {
|
||||
let {scene,mesh,frag,id} = opts
|
||||
const THREE = xrf.THREE
|
||||
let oldSelection
|
||||
// Selection of Interest if predefined_view matches object name
|
||||
if( mesh.visible && mesh.material){
|
||||
|
@ -35,8 +36,9 @@ xrf.drawLineToMesh = (opts) => {
|
|||
return center;
|
||||
}
|
||||
|
||||
xrf.camera.getCam().updateMatrixWorld(true); // always keeps me diving into the docs :]
|
||||
xrf.camera.getCam().getWorldPosition(from)
|
||||
let cam = xrf.camera.getCam ? xrf.camera.getCam() : xrf.camera // *FIXME* camerarig/rig are conflicting
|
||||
cam.updateMatrixWorld(true); // always keeps me diving into the docs :]
|
||||
cam.getWorldPosition(from)
|
||||
from.y = 0.5 // originate from the heart chakra! :p
|
||||
const points = [from, getCenterPoint(mesh) ]
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints( points );
|
||||
|
|
|
@ -41,7 +41,6 @@ xrf.frag.href = function(v, opts){
|
|||
let click = mesh.userData.XRF.href.exec = (e) => {
|
||||
|
||||
let lastPos = `pos=${camera.position.x.toFixed(2)},${camera.position.y.toFixed(2)},${camera.position.z.toFixed(2)}`
|
||||
|
||||
xrf
|
||||
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
|
||||
.then( () => {
|
||||
|
@ -60,7 +59,12 @@ xrf.frag.href = function(v, opts){
|
|||
let newState = o.name == mesh.name ? state : false
|
||||
if( o.material ){
|
||||
if( o.material.uniforms ) o.material.uniforms.selected.value = newState
|
||||
if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
|
||||
//if( o.material.emissive ) o.material.emissive.r = o.material.emissive.g = o.material.emissive.b = newState ? 2.0 : 1.0
|
||||
if( o.material.emissive ){
|
||||
if( !o.material.emissive.original ) o.material.emissive.original = o.material.emissive.clone()
|
||||
o.material.emissive.r = o.material.emissive.g = o.material.emissive.b =
|
||||
newState ? o.material.emissive.original.r + 0.2 : o.material.emissive.original.r
|
||||
}
|
||||
}
|
||||
})
|
||||
// update mouse cursor
|
||||
|
@ -75,9 +79,10 @@ xrf.frag.href = function(v, opts){
|
|||
|
||||
mesh.addEventListener('click', click )
|
||||
mesh.addEventListener('mousemove', selected(true) )
|
||||
mesh.addEventListener('mouseenter', selected(true) )
|
||||
mesh.addEventListener('mouseleave', selected(false) )
|
||||
|
||||
if( isLocal && isPlane && !hasSrc ) xrf.portalNonEuclidian(v,opts)
|
||||
if( isLocal && isPlane && !hasSrc && !mesh.material.map ) xrf.portalNonEuclidian(v,opts)
|
||||
|
||||
// lazy add mesh (because we're inside a recursive traverse)
|
||||
setTimeout( (mesh) => {
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
xrf.portalNonEuclidian = function(v, opts){
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
|
||||
let toFrag = xrf.URI.parse( v.string )
|
||||
|
||||
// turn plane into stencilplane
|
||||
mesh.material = new THREE.MeshPhongMaterial({ color: 'green' });
|
||||
mesh.material.depthWrite = false;
|
||||
mesh.material.stencilWrite = true;
|
||||
mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef;
|
||||
mesh.material.stencilFunc = THREE.AlwaysStencilFunc;
|
||||
mesh.material.stencilZPass = THREE.ReplaceStencilOp;
|
||||
mesh.cam = xrf.camera.getCam().clone()
|
||||
mesh.cam.position.x = toFrag.pos.x
|
||||
mesh.cam.position.y = toFrag.pos.y + 1
|
||||
mesh.cam.position.z = toFrag.pos.z
|
||||
mesh.isStencil = true
|
||||
|
||||
|
||||
mesh.onBeforeRender = function( renderer, scene, camera, geometry, material, group ){
|
||||
xrf.scene.traverse( (n) => !n.isStencil && n.material ? n.material.stencilRef = this.material.stencilRef : false )
|
||||
xrf.renderer.render( xrf.scene, this.cam )
|
||||
}
|
||||
|
||||
mesh.onAfterRender = function( renderer, scene, camera, geometry, material, group ){
|
||||
xrf.scene.traverse( (n) => !n.isStencil && n.material ? n.material.stencilRef = 0 : false )
|
||||
}
|
||||
|
||||
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
|
||||
// OBJECT INSIDE CUBE
|
||||
//objectMat.stencilWrite = true;
|
||||
//objectMat.stencilRef = stencilRef;
|
||||
//objectMat.stencilFunc = THREE.EqualStencilFunc;
|
||||
//const object = new THREE.Mesh(objectGeom, objectMat);
|
||||
//scene.add(object);
|
||||
console.log("enabling stencil plane")
|
||||
|
||||
|
||||
}
|
||||
|
||||
xrf.portalNonEuclidian.stencilRef = 1
|
|
@ -1,7 +1,6 @@
|
|||
// *TODO* use webgl instancing
|
||||
|
||||
xrf.frag.src = function(v, opts){
|
||||
|
||||
opts.embedded = v // indicate embedded XR fragment
|
||||
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
|
||||
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
*/
|
||||
|
||||
let loadAudio = (mimetype) => function(url,opts){
|
||||
let {mesh,src,camera} = opts
|
||||
let {mesh,src,camera,THREE} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let frag = xrf.URI.parse( url )
|
||||
|
||||
/* WebAudio: setup context via THREEjs */
|
||||
if( !camera.listener ){
|
||||
camera.listener = new THREE.AudioListener();
|
||||
camera.getCam().add( camera.listener );
|
||||
// *FIXME* camera vs camerarig conflict
|
||||
(camera.getCam ? camera.getCam() : camera).add( camera.listener );
|
||||
}
|
||||
|
||||
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
xrf.frag.src.type['image/png'] = function(url,opts){
|
||||
let {mesh} = opts
|
||||
let {mesh,THREE} = opts
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
|
||||
let renderEquirect = (texture) => {
|
||||
|
@ -73,7 +73,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
|
|||
}
|
||||
}
|
||||
//const geometry = new THREE.BoxGeometry();
|
||||
mesh.material = new THREE.MeshBasicMaterial({
|
||||
mesh.material = new xrf.THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
transparent: url.match(/(png|gif)/) ? true : false,
|
||||
side: THREE.DoubleSide,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
let loadVideo = (mimetype) => function(url,opts){
|
||||
let {mesh,src,camera} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
const THREE = xrf.THREE
|
||||
let frag = xrf.URI.parse( url )
|
||||
|
||||
let video = mesh.video = document.createElement('video')
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
xrf.addEventListener('env', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
||||
if( frag.env && !scene.environment ){
|
||||
let env = scene.getObjectByName(frag.env.string)
|
||||
if( !env ) env = xrf.scene.getObjectByName(frag.env.string) // repurpose from parent scene
|
||||
if( !env ) return console.warn("xrf.env "+frag.env.string+" not found")
|
||||
env.material.map.mapping = THREE.EquirectangularReflectionMapping;
|
||||
scene.environment = env.material.map
|
||||
//let env = scene.getObjectByName(frag.env.string)
|
||||
//if( !env ) env = xrf.scene.getObjectByName(frag.env.string) // repurpose from parent scene
|
||||
//if( !env ) return console.warn("xrf.env "+frag.env.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;
|
||||
// renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
// renderer.toneMappingExposure = 2;
|
||||
console.log(` └ applied image '${frag.env.string}' as environment map`)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
xrf.macros = {}
|
||||
|
||||
xrf.addEventListener('mesh', (opts) => {
|
||||
xrf.addEventListener('frag2mesh', (opts) => {
|
||||
let { frag, mesh, model, camera, scene, renderer, THREE, hashbus} = opts
|
||||
|
||||
for( let k in frag ){
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title>AFRAME - xrfragment sandbox</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||
<script src="./../../dist/xrfragment.aframe.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
<script src="./pubsub.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h3>open the browserconsole to see the results</h3>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
// in the browser use this instead of require():
|
||||
//
|
||||
// <script src="dist/xrfragment.js"></script>
|
||||
// <script>
|
||||
// var XRF = xrfragment;
|
||||
// </script>
|
||||
|
||||
|
||||
console.assert = ((assert) => (a,b) => {
|
||||
console.log('♥ test: '+b.reason)
|
||||
assert.call( console, a, b )
|
||||
})(console.assert)
|
||||
|
||||
/*
|
||||
* parser checks
|
||||
*/
|
||||
let frags = xrf.URI.parse('://foo.com/1.gltf#pos=1.0,2.0,3.0&q=-.foo&t=1,100',true)
|
||||
console.assert( frags.t, {frags, reason:'URI.parse(): t needs to be set'})
|
||||
|
||||
let q = new xrf.Query();
|
||||
console.assert( new xrf.Query(), {reason: 'new Query() should be available'})
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
// test simple event
|
||||
opts = {a:0}
|
||||
xrf.addEventListener('foo1', (opts) => opts.a = 1 )
|
||||
xrf.emit('foo1',opts)
|
||||
setTimeout( () => console.assert( opts.a, {opts, reason:"xrf.emit('foo1',...) should set a to 1"}), 100)
|
||||
|
||||
// test simple promise event
|
||||
opts = {a:0}
|
||||
xrf.addEventListener('foo2', (e) => {
|
||||
let p = e.promise()
|
||||
e.a = 1
|
||||
p.resolve(e)
|
||||
})
|
||||
xrf.emit('foo2',opts)
|
||||
.then( (opts) => {
|
||||
console.assert( opts.a, {opts, reason:"xrf.emit('foo2',...) should set a to 1 via promise"})
|
||||
})
|
||||
|
||||
// test multiple promise event
|
||||
opts = {a:""}
|
||||
xrf.addEventListener('foo3', (e) => {
|
||||
let p = e.promise()
|
||||
setTimeout( () => {
|
||||
e.a += "1"
|
||||
p.resolve(e)
|
||||
}, 100 )
|
||||
})
|
||||
xrf.addEventListener('foo3', (e) => {
|
||||
let p = e.promise()
|
||||
e.a += "2"
|
||||
p.resolve(e)
|
||||
})
|
||||
xrf.emit('foo3',opts)
|
||||
.then( (opts) => {
|
||||
opts.a += "3"
|
||||
console.assert( opts.a == "213", {opts, reason:"xrf.emit('foo3',...) should support multiple promise listeners"})
|
||||
})
|
Loading…
Reference in New Issue