diff --git a/example/assets/index.glb b/example/assets/index.glb index 2f3cdbf..3ce01b5 100644 Binary files a/example/assets/index.glb and b/example/assets/index.glb differ diff --git a/src/3rd/js/three/index.js b/src/3rd/js/three/index.js index a74c6a6..6a80602 100644 --- a/src/3rd/js/three/index.js +++ b/src/3rd/js/three/index.js @@ -24,7 +24,6 @@ xrf.patchRenderer = function(opts){ renderer.render = ((render) => function(scene,camera){ // 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,render}) // allow fragments to do something at renderframe render(scene,camera) xrf.emit('renderPost',{scene,camera,time,render,renderer}) // allow fragments to do something after renderframe diff --git a/src/3rd/js/three/xrf/dynamic/filter.js b/src/3rd/js/three/xrf/dynamic/filter.js index 510e9cf..1b9081f 100644 --- a/src/3rd/js/three/xrf/dynamic/filter.js +++ b/src/3rd/js/three/xrf/dynamic/filter.js @@ -60,7 +60,6 @@ xrf.filter.process = function(frag,scene,opts){ const setVisible = (n,visible,filter,processed) => { if( processed && processed[n.uuid] ) return getOrCloneMaterial(n).visible = visible - console.log(n.name+" => "+(visible?"show":"hide")) if( filter.deep ) n.traverse( (m) => getOrCloneMaterial(m).visible = visible ) if( processed ) processed[n.uuid] == true } diff --git a/src/3rd/js/three/xrf/src.js b/src/3rd/js/three/xrf/src.js index a6ba327..3ee62da 100644 --- a/src/3rd/js/three/xrf/src.js +++ b/src/3rd/js/three/xrf/src.js @@ -20,8 +20,11 @@ xrf.frag.src.addModel = (model,url,frag,opts) => { if( mesh.material ) mesh.material.visible = false // hide placeholder object //enableSourcePortation(scene) if( xrf.frag.src.renderAsPortal(mesh) ){ - if( !opts.isLocal ) xrf.scene.add(scene) - return xrf.portalNonEuclidian({...opts,model,scene:model.scene}) + // 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 }else{ xrf.frag.src.scale( scene, opts, url ) // scale scene mesh.add(scene) diff --git a/src/3rd/js/three/xrf/src/non-euclidian.js b/src/3rd/js/three/xrf/src/non-euclidian.js index 99789d8..8657d1c 100644 --- a/src/3rd/js/three/xrf/src/non-euclidian.js +++ b/src/3rd/js/three/xrf/src/non-euclidian.js @@ -3,7 +3,6 @@ xrf.portalNonEuclidian = function(opts){ let { frag, mesh, model, camera, scene, renderer} = opts - mesh.portal = { pos: mesh.position.clone(), posWorld: new xrf.THREE.Vector3(), @@ -13,18 +12,22 @@ xrf.portalNonEuclidian = function(opts){ stencilObject: false, cameraDirection: new THREE.Vector3(), cameraPosition: new THREE.Vector3(), - raycaster: new THREE.Raycaster() + raycaster: new THREE.Raycaster(), + isLocal: opts.isLocal, + isLens: false } // allow objects to flip between original and stencil position (which puts them behind stencilplane) const addStencilFeature = (n) => { if( n.stencil ) return n // run once n.stencil = ( (pos,scale) => (sRef,newPos, newScale) => { - n.position.copy( sRef == 0 ? pos : newPos ) - if( sRef > 0 ) n.scale.multiply( newScale ) - else n.scale.copy( scale ) + if( !mesh.portal.isLens ){ + n.position.copy( sRef == 0 ? pos : newPos ) + if( sRef > 0 ) n.scale.multiply( newScale ) + else n.scale.copy( scale ) + n.updateMatrixWorld(true) + } xrf.portalNonEuclidian.selectStencil(n, sRef ) - n.updateMatrixWorld(true) } )( n.position.clone(), n.scale.clone() ) return n @@ -32,14 +35,22 @@ xrf.portalNonEuclidian = function(opts){ this.setupStencilObjects = (scene,opts) => { // collect related objects to render inside stencilplane - let stencilObject = opts.srcFrag.target ? scene.getObjectByName( opts.srcFrag.target.key ) : scene // strip # + let stencilObject = scene + if( opts.srcFrag.target ){ + stencilObject = scene.getObjectByName( opts.srcFrag.target.key ) + // scan if object is child of portal (then project lens) + mesh.traverse( (n) => n.name == opts.srcFrag.target.key && (stencilObject = n) && (mesh.portal.isLens = true) ) + } if( !stencilObject ) return console.warn(`no objects were found (src:${mesh.userData.src}) for (portal)object name '${mesh.name}'`) - if( !opts.isLocal ) stencilObject.visible = false - let stencilObjects = [mesh,stencilObject] + mesh.portal.stencilObject = stencilObject + + // spec: if src points to child, act as lens + if( !mesh.portal.isLocal || mesh.portal.isLens ) stencilObject.visible = false + + let stencilObjects = [stencilObject] stencilObjects = stencilObjects .filter( (n) => !n.portal ) // filter out (self)references to portals (prevent recursion) .map(addStencilFeature) - mesh.portal.stencilObject = stencilObject //// add missing lights to make sure things get lit properly xrf.scene.traverse( (n) => n.isLight && @@ -70,16 +81,8 @@ xrf.portalNonEuclidian = function(opts){ this.setupListeners = () => { - mesh.onBeforeRender = function(renderer, scene, camera, geometry, material, group ){ - } - mesh.onAfterRender = function(renderer, scene, camera, geometry, material, group ){ - mesh.portal.needUpdate = true - } - - xrf.addEventListener('renderPost', (opts) => { - if( mesh.portal && mesh.portal.needUpdate && mesh.portal.stencilObjects ){ - let {scene,camera,time,render} = opts + if( mesh.portal && mesh.portal.stencilObjects ){ let stencilRef = mesh.portal.stencilRef let newPos = mesh.portal.posWorld let stencilObject = mesh.portal.stencilObject @@ -89,15 +92,14 @@ xrf.portalNonEuclidian = function(opts){ let raycaster = mesh.portal.raycaster // init - if( !opts.isLocal ) stencilObject.visible = true + if( !mesh.portal.isLocal || mesh.portal.isLens ) stencilObject.visible = true mesh.portal.stencilObjects.traverse( (n) => showPortal(n,false) && n.stencil && n.stencil(stencilRef,newPos,newScale) ) renderer.autoClear = false - renderer.clearDepth() // render - render( mesh.portal.stencilObjects, camera ) + renderer.render( mesh.portal.stencilObjects, camera ) // de-init mesh.portal.stencilObjects.traverse( (n) => showPortal(n,true) && n.stencil && (n.stencil(0)) ) - if( !opts.isLocal ) stencilObject.visible = false + if( !mesh.portal.isLocal || mesh.portal.isLens ) stencilObject.visible = false // trigger href upon camera collide @@ -114,10 +116,8 @@ xrf.portalNonEuclidian = function(opts){ setTimeout( () => mesh.portal.teleporting = false, 500) // dont flip back and forth } } - - mesh.portal.needUpdate = false } - }) + } return this } @@ -126,6 +126,7 @@ xrf.portalNonEuclidian = function(opts){ .portalNonEuclidian .setMaterial(mesh) .getWorldPosition(mesh.portal.posWorld) + mesh.portal.posWorld.y +=0.2 this .setupListeners() @@ -143,23 +144,23 @@ xrf.portalNonEuclidian.selectStencil = (n, stencilRef, nested) => { } xrf.portalNonEuclidian.setMaterial = function(mesh){ - mesh.material = new xrf.THREE.MeshBasicMaterial({ color: 'white' }); + mesh.material = new xrf.THREE.MeshBasicMaterial({ color: 'orange' }); mesh.material.depthWrite = false; - mesh.material.depthTest = false; mesh.material.colorWrite = false; mesh.material.stencilWrite = true; mesh.material.stencilRef = xrf.portalNonEuclidian.stencilRef; - mesh.renderOrder = 0;//xrf.portalNonEuclidian.stencilRef; - mesh.material.stencilFunc = THREE.AlwaysStencilFunc; - mesh.material.stencilZPass = THREE.ReplaceStencilOp; - //mesh.material.stencilFail = THREE.ReplaceStencilOp; - //mesh.material.stencilZFail = THREE.ReplaceStencilOp; + mesh.renderOrder = 10;//xrf.portalNonEuclidian.stencilRef; + mesh.material.stencilFunc = xrf.THREE.AlwaysStencilFunc; + mesh.material.stencilZPass = xrf.THREE.ReplaceStencilOp; + mesh.material.stencilZFail = xrf.THREE.ReplaceStencilOp; + //n.material.depthFunc = stencilRef > 0 ? xrf.THREE.AlwaysDepth : xrf.THREE.LessEqualDepth + //mesh.material.depthTest = false; return mesh } xrf.addEventListener('parseModel',(opts) => { const scene = opts.model.scene - scene.traverse( (n) => n.renderOrder = 10 ) // rendering everything *after* the stencil buffers + scene.traverse( (n) => n.renderOrder = 0 ) // rendering everything *after* the stencil buffers })