update non-euclidian
This commit is contained in:
parent
ff05234606
commit
29744defef
10 changed files with 131 additions and 173 deletions
File diff suppressed because one or more lines are too long
|
|
@ -34,8 +34,13 @@ window.AFRAME.registerComponent('xrf', {
|
|||
let blinkControls = document.querySelector('[blink-controls]')
|
||||
if( blinkControls ){
|
||||
let els = xrf.getCollisionMeshes()
|
||||
let invisible = false
|
||||
els.map( (mesh) => {
|
||||
mesh.material.visible = false
|
||||
if( !invisible ){
|
||||
invisible = mesh.material.clone()
|
||||
invisible.visible = false
|
||||
}
|
||||
mesh.material = invisible
|
||||
let el = document.createElement("a-entity")
|
||||
el.setAttribute("xrf-get", mesh.name )
|
||||
el.setAttribute("class","floor")
|
||||
|
|
@ -124,21 +129,6 @@ window.AFRAME.registerComponent('xrf', {
|
|||
els.map( (el) => document.querySelector('a-scene').removeChild(el) )
|
||||
})
|
||||
|
||||
// *TODO* workaround no longer needed?
|
||||
//
|
||||
// aScene.addEventListener('enter-vr', () => {
|
||||
// // undo lookup-control shenanigans (which blocks updating camerarig position in VR)
|
||||
// document.querySelector('[camera]').object3D.parent.matrixAutoUpdate = true
|
||||
// document.querySelector('[camera]').components['look-controls'].pause() //removeAttribute("look-controls")
|
||||
// document.querySelector('[camera]').components['wasd-controls'].pause() //removeAttribute("wasd-controls")
|
||||
// })
|
||||
// aScene.addEventListener('exit-vr', () => {
|
||||
// // redo lookup-control shenanigans (which blocks updating camerarig position in VR)
|
||||
// document.querySelector('[camera]').object3D.parent.matrixAutoUpdate = false
|
||||
// document.querySelector('[camera]').components['look-controls'].play() //setAttribute("look-controls",'')
|
||||
// document.querySelector('[camera]').components['wasd-controls'].play() //setAttribute("wasd-controls",'')
|
||||
// })
|
||||
|
||||
AFRAME.XRF.navigator.to(this.data)
|
||||
.then( (model) => {
|
||||
let gets = [ ...document.querySelectorAll('[xrf-get]') ]
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ xrf.drawLineToMesh = (opts) => {
|
|||
}
|
||||
}
|
||||
|
||||
xrf.addEventListener('navigateLoaded', (opts) => {
|
||||
xrf.addEventListener('render', (opts) => {
|
||||
xrf.addEventListener('render', (opts) => {
|
||||
// update focusline
|
||||
let {time,model} = opts
|
||||
if( !xrf.clock ) return
|
||||
|
|
@ -62,5 +61,4 @@ xrf.addEventListener('navigateLoaded', (opts) => {
|
|||
xrf.focusLine.material.opacity = (0.25 + 0.15*Math.sin( xrf.clock.getElapsedTime() * 3 )) * xrf.focusLine.opacity;
|
||||
if( xrf.focusLine.opacity > 0.0 ) xrf.focusLine.opacity -= time*0.2
|
||||
if( xrf.focusLine.opacity < 0.0 ) xrf.focusLine.opacity = 0
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ xrf.filter = function(query, cb){
|
|||
xrf.filter.scene = function(opts){
|
||||
let {scene,frag} = opts
|
||||
|
||||
xrf.filter
|
||||
scene = xrf.filter
|
||||
.sort(frag) // get (sorted) filters from XR Fragments
|
||||
.process(frag,scene,opts) // show/hide things
|
||||
|
||||
|
|
@ -41,12 +41,14 @@ xrf.filter.sort = function(frag){
|
|||
return xrf.filter
|
||||
}
|
||||
|
||||
// opts = {copyScene:true} in case you want a copy of the scene (not filter the current scene inplace)
|
||||
xrf.filter.process = function(frag,scene,opts){
|
||||
const cleanupKey = (k) => k.replace(/[-\*\/]/g,'')
|
||||
let firstFilter = frag.filters.length ? frag.filters[0].filter.get() : false
|
||||
const hasName = (m,name,filter) => m.name == name
|
||||
const hasNameOrTag = (m,name_or_tag,filter) => hasName(m,name_or_tag) ||
|
||||
String(m.userData['tag']).match( new RegExp("(^| )"+name_or_tag) )
|
||||
|
||||
// utility functions
|
||||
const getOrCloneMaterial = (o) => {
|
||||
if( o.material ){
|
||||
|
|
@ -70,12 +72,20 @@ xrf.filter.process = function(frag,scene,opts){
|
|||
let obj
|
||||
frag.target = firstFilter
|
||||
scene.traverse( (n) => hasName(n, firstFilter.key,firstFilter) && (obj = n) )
|
||||
if(obj){
|
||||
while( scene.children.length > 0 ) scene.children[0].removeFromParent()
|
||||
console.log("reparent "+firstFilter.key+" "+((opts.copyScene)?"copy":"inplace"))
|
||||
if(obj ){
|
||||
obj.position.set(0,0,0)
|
||||
if( opts.copyScene ){
|
||||
opts.copyScene = new xrf.THREE.Scene()
|
||||
opts.copyScene.children[0] = obj
|
||||
scene = opts.copyScene
|
||||
}else{
|
||||
// empty current scene and add obj
|
||||
while( scene.children.length > 0 ) scene.children[0].removeFromParent()
|
||||
scene.add( obj )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then show/hide things based on secondary selectors
|
||||
// we don't use the XRWG (everything) because we process only the given (sub)scene
|
||||
|
|
@ -88,7 +98,7 @@ xrf.filter.process = function(frag,scene,opts){
|
|||
// hide external objects temporarely
|
||||
scene.traverse( (m) => {
|
||||
if( m.isSRCExternal ){
|
||||
m.traverse( (n) => (extembeds[ n.uuid ] = m) && (n.visible = false) )
|
||||
m.traverse( (n) => (extembeds[ n.uuid ] = m) && (m.visible = false) )
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -108,6 +118,6 @@ xrf.filter.process = function(frag,scene,opts){
|
|||
for ( let i in extembeds ) extembeds[i].visible = true
|
||||
})
|
||||
|
||||
return xrf.filter
|
||||
return scene
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,13 @@ xrf.frag.href = function(v, opts){
|
|||
xrf
|
||||
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
|
||||
.then( () => {
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(v.string)
|
||||
//if( !file.match(/\./) || file.match(/\.html/) ){
|
||||
// debugger
|
||||
// let inIframe
|
||||
// try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; }
|
||||
// return inIframe ? window.parent.postMessage({ url: v.string }, '*') : window.open( v.string, '_blank')
|
||||
//}
|
||||
const flags = v.string[0] == '#' ? xrf.XRF.PV_OVERRIDE : undefined
|
||||
let toFrag = xrf.URI.parse( v.string, xrf.XRF.NAVIGATOR | xrf.XRF.PV_OVERRIDE | xrf.XRF.METADATA )
|
||||
// always commit current location (keep a trail of last positions before we navigate)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ xrf.frag.src = function(v, opts){
|
|||
let url = v.string
|
||||
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url)
|
||||
opts.isLocal = v.string[0] == '#'
|
||||
opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
|
||||
|
||||
if( opts.isLocal ){
|
||||
xrf.frag.src.localSRC(url,srcFrag,opts) // local
|
||||
|
|
@ -16,22 +17,21 @@ xrf.frag.src = function(v, opts){
|
|||
xrf.frag.src.addModel = (model,url,frag,opts) => {
|
||||
let {mesh} = opts
|
||||
let scene = model.scene
|
||||
xrf.frag.src.filterScene(scene,{...opts,frag}) // filter scene
|
||||
if( mesh.material ) mesh.material.visible = false // hide placeholder object
|
||||
scene = xrf.frag.src.filterScene(scene,{...opts,frag}) // get filtered scene
|
||||
if( mesh.material && !mesh.userData.src ) mesh.material.visible = false // hide placeholder object
|
||||
//enableSourcePortation(scene)
|
||||
if( xrf.frag.src.renderAsPortal(mesh) ){
|
||||
// only add remote objects, because
|
||||
// local scene-objects are already added to scene
|
||||
xrf.portalNonEuclidian({...opts,model,scene:model.scene})
|
||||
if( !opts.isLocal && !mesh.portal.isLens ) xrf.scene.add(scene)
|
||||
return
|
||||
if( !opts.isLocal ) xrf.scene.add(scene)
|
||||
}else{
|
||||
xrf.frag.src.scale( scene, opts, url ) // scale scene
|
||||
mesh.add(scene)
|
||||
xrf.emit('parseModel', {...opts, scene, model})
|
||||
}
|
||||
// flag everything isSRC & isXRF
|
||||
mesh.traverse( (n) => { n.isSRC = n.isXRF = n[ opts.isLocal ? 'isSRCLocal' : 'isSRCExternal' ] = true })
|
||||
xrf.emit('parseModel', {...opts, scene, model})
|
||||
}
|
||||
|
||||
xrf.frag.src.renderAsPortal = (mesh) => {
|
||||
|
|
@ -77,13 +77,16 @@ xrf.frag.src.externalSRC = (url,frag,opts) => {
|
|||
}
|
||||
|
||||
xrf.frag.src.localSRC = (url,frag,opts) => {
|
||||
let {model,scene} = opts
|
||||
let {model,mesh,scene} = opts
|
||||
setTimeout( () => {
|
||||
if( mesh.material ) mesh.material = mesh.material.clone() // clone, so we can individually highlight meshes
|
||||
let _model = {
|
||||
animations: model.animations,
|
||||
scene: scene.clone()
|
||||
scene: scene.clone() // *TODO* opts.isPortal ? scene : scene.clone()
|
||||
}
|
||||
_model.scenes = [_model.scene]
|
||||
xrf.frag.src.addModel(_model,url,frag, opts) // current file
|
||||
},500 )
|
||||
}
|
||||
|
||||
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
|
||||
|
|
@ -121,12 +124,14 @@ xrf.frag.src.scale = function(scene, opts, url){
|
|||
xrf.frag.src.filterScene = (scene,opts) => {
|
||||
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
|
||||
|
||||
xrf.filter.scene({scene,frag,reparent:true})
|
||||
scene = xrf.filter.scene({scene,frag,reparent:true}) // *TODO* ,copyScene: opts.isPortal})
|
||||
|
||||
if( !opts.isLocal ){
|
||||
scene.traverse( (m) => {
|
||||
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
|
||||
hashbus.pub.mesh(m,{scene,recursive:true}) // cool idea: recursion-depth based distance between face & src
|
||||
})
|
||||
}
|
||||
return scene
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let frag = xrf.URI.parse( url )
|
||||
|
||||
return
|
||||
|
||||
/* WebAudio: setup context via THREEjs */
|
||||
if( !camera.listener ){
|
||||
camera.listener = new THREE.AudioListener();
|
||||
|
|
@ -37,7 +35,8 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
}
|
||||
|
||||
sound.playXRF = (t) => {
|
||||
|
||||
mesh.add(sound)
|
||||
try{
|
||||
if( sound.isPlaying && t.y != undefined ) sound.stop()
|
||||
if( sound.isPlaying && t.y == undefined ) sound.pause()
|
||||
|
||||
|
|
@ -61,11 +60,10 @@ let loadAudio = (mimetype) => function(url,opts){
|
|||
}
|
||||
sound.play()
|
||||
}
|
||||
}catch(e){ console.warn(e) }
|
||||
}
|
||||
mesh.add(sound)
|
||||
});
|
||||
|
||||
mesh.audio = sound
|
||||
});
|
||||
}
|
||||
|
||||
let audioMimeTypes = [
|
||||
|
|
|
|||
12
src/3rd/js/three/xrf/src/html.js
Normal file
12
src/3rd/js/three/xrf/src/html.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
let loadHTML = (mimetype) => function(url,opts){
|
||||
let {mesh,src,camera} = opts
|
||||
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
|
||||
let frag = xrf.URI.parse( url )
|
||||
console.warn("todo: html viewer for src not implemented")
|
||||
}
|
||||
|
||||
let htmlMimeTypes = [
|
||||
'text/html'
|
||||
]
|
||||
htmlMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadHTML(mimetype) )
|
||||
|
|
@ -8,55 +8,13 @@ xrf.frag.src.type['image/png'] = function(url,opts){
|
|||
let {mesh,THREE} = opts
|
||||
let restrictTo3DBoundingBox = mesh.geometry
|
||||
|
||||
let renderEquirect = (texture) => {
|
||||
texture.mapping = THREE.EquirectangularReflectionMapping
|
||||
texture.needsUpdate = true
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.magFilter = THREE.NearestFilter
|
||||
texture.minFilter = THREE.NearestFilter
|
||||
|
||||
// poor man's equi-portal
|
||||
mesh.material = new THREE.ShaderMaterial( {
|
||||
mesh.material = new xrf.THREE.MeshBasicMaterial({
|
||||
map: null,
|
||||
transparent: url.match(/(png|gif)/) ? true : false,
|
||||
side: THREE.DoubleSide,
|
||||
uniforms: {
|
||||
pano: { value: texture },
|
||||
selected: { value: false },
|
||||
},
|
||||
vertexShader: `
|
||||
vec3 portalPosition;
|
||||
varying vec3 vWorldPosition;
|
||||
varying float vDistanceToCenter;
|
||||
varying float vDistance;
|
||||
void main() {
|
||||
vDistanceToCenter = clamp(length(position - vec3(0.0, 0.0, 0.0)), 0.0, 1.0);
|
||||
portalPosition = (modelMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
||||
vDistance = length(portalPosition - cameraPosition);
|
||||
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
#define RECIPROCAL_PI2 0.15915494
|
||||
uniform sampler2D pano;
|
||||
uniform bool selected;
|
||||
varying float vDistanceToCenter;
|
||||
varying float vDistance;
|
||||
varying vec3 vWorldPosition;
|
||||
void main() {
|
||||
vec3 direction = normalize(vWorldPosition - cameraPosition );
|
||||
vec2 sampleUV;
|
||||
sampleUV.y = clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2;
|
||||
sampleUV.x += 0.33; // adjust focus to AFRAME's a-scene.components.screenshot.capture()
|
||||
vec4 color = texture2D(pano, sampleUV);
|
||||
vec4 selected_color = selected ? color*vec4(1.5) : color;
|
||||
gl_FragColor = selected_color;
|
||||
}
|
||||
`,
|
||||
color: 0xFFFFFF,
|
||||
opacity:1
|
||||
});
|
||||
mesh.material.needsUpdate = true
|
||||
}
|
||||
|
||||
let renderImage = (texture) => {
|
||||
let img = {w: texture.source.data.width, h: texture.source.data.height}
|
||||
|
|
@ -72,27 +30,16 @@ xrf.frag.src.type['image/png'] = function(url,opts){
|
|||
//}
|
||||
}
|
||||
}
|
||||
//const geometry = new THREE.BoxGeometry();
|
||||
mesh.material = new xrf.THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
transparent: url.match(/(png|gif)/) ? true : false,
|
||||
side: THREE.DoubleSide,
|
||||
color: 0xFFFFFF,
|
||||
opacity:1
|
||||
});
|
||||
mesh.material.map = texture
|
||||
mesh.needsUpdate = true
|
||||
}
|
||||
|
||||
let onLoad = (texture) => {
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
// detect equirectangular image
|
||||
if( texture && texture.source.data.height == texture.source.data.width/2 ){
|
||||
renderEquirect(texture)
|
||||
}else{
|
||||
renderImage(texture)
|
||||
}
|
||||
}
|
||||
|
||||
new THREE.TextureLoader().load( url, onLoad, null, console.error );
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
xrf.portalNonEuclidian = function(opts){
|
||||
let { frag, mesh, model, camera, scene, renderer} = opts
|
||||
|
||||
|
||||
mesh.portal = {
|
||||
pos: mesh.position.clone(),
|
||||
posWorld: new xrf.THREE.Vector3(),
|
||||
|
|
@ -55,12 +56,6 @@ xrf.portalNonEuclidian = function(opts){
|
|||
.filter( (n) => !n.portal ) // filter out (self)references to portals (prevent recursion)
|
||||
.map(addStencilFeature)
|
||||
|
||||
//// add missing lights to make sure things get lit properly
|
||||
xrf.scene.traverse( (n) => n.isLight &&
|
||||
!stencilObjects.find( (o) => o.uuid == n.uuid ) &&
|
||||
stencilObjects.push(n)
|
||||
)
|
||||
|
||||
// put it into a scene (without .add() because it reparents objects) so we can render it separately
|
||||
mesh.portal.stencilObjects = new xrf.THREE.Scene()
|
||||
mesh.portal.stencilObjects.children = stencilObjects
|
||||
|
|
@ -68,11 +63,6 @@ xrf.portalNonEuclidian = function(opts){
|
|||
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
|
||||
console.log(`enabling portal for object '${mesh.name}' (stencilRef:${mesh.portal.stencilRef})`)
|
||||
|
||||
// clone so it won't be affected by other fragments
|
||||
setTimeout( (mesh) => {
|
||||
if( mesh.material ) mesh.material = mesh.material.clone() // clone, so we can individually highlight meshes
|
||||
}, 0, mesh )
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +144,7 @@ xrf.portalNonEuclidian = function(opts){
|
|||
.setupStencilObjects(scene,opts)
|
||||
|
||||
// move portal objects to portalposition
|
||||
mesh.portal.positionObjectsIfNeeded(mesh.portal.posWorld, mesh.scale)
|
||||
if( mesh.portal.stencilObjects ) mesh.portal.positionObjectsIfNeeded(mesh.portal.posWorld, mesh.scale)
|
||||
}
|
||||
|
||||
xrf.portalNonEuclidian.selectStencil = (n, stencilRef, nested) => {
|
||||
|
|
@ -183,24 +173,25 @@ xrf.portalNonEuclidian.setMaterial = function(mesh){
|
|||
|
||||
xrf.addEventListener('parseModel',(opts) => {
|
||||
const scene = opts.model.scene
|
||||
// scene.traverse( (n) => n.renderOrder = 10 ) // rendering everything *after* the stencil buffers
|
||||
//for( let i in scene.children ) scene.children[i].renderOrder = 10 // render outer layers last (worldspheres e.g.)
|
||||
})
|
||||
|
||||
|
||||
// (re)set portalObject when entering/leaving a portal
|
||||
xrf.addEventListener('href', (opts) => {
|
||||
let {mesh,v} = opts
|
||||
if( opts.click && mesh.portal ){
|
||||
if( !opts.click ) return
|
||||
// (re)set portalObjects when entering/leaving a portal
|
||||
let updatePortals = (opts) => {
|
||||
xrf.scene.traverse( (n) => {
|
||||
if( !n.portal ) return
|
||||
// since we're leaving this portal destination, lets move objects back to the portal
|
||||
// move objects back to the portal
|
||||
if( n.portal.isInside ) n.portal.positionObjectsIfNeeded( n.portal.posWorld, n.scale )
|
||||
n.portal.isInside = false
|
||||
})
|
||||
mesh.portal.isInside = true
|
||||
mesh.portal.positionObjectsIfNeeded() // move objects back to original pos (since we are going there)
|
||||
if( opts.mesh && opts.mesh.portal && opts.click ){
|
||||
opts.mesh.portal.isInside = true
|
||||
opts.mesh.portal.positionObjectsIfNeeded() // move objects back to original pos (since we are teleporting there)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
xrf.addEventListener('href', (opts) => opts.click && updatePortals(opts) )
|
||||
xrf.addEventListener('navigate', updatePortals )
|
||||
|
||||
xrf.portalNonEuclidian.stencilRef = 1
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue