84 lines
3.5 KiB
JavaScript
84 lines
3.5 KiB
JavaScript
|
|
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 // *TODO* this requires flipping normals based on camera orientation
|
||
|
|
mesh.portal = {
|
||
|
|
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, stencilMat, mat ) => (enabled) => {
|
||
|
|
let sRef = enabled ? mesh.portal.stencilRef : 0
|
||
|
|
stencilMat.depthTest = false
|
||
|
|
n.position.copy( enabled ? stencilPos : pos )
|
||
|
|
n.material = enabled ? stencilMat : mat
|
||
|
|
xrf.portalNonEuclidian.selectStencil(n, sRef ) // disable depthtest of world-container (sky e.g.)
|
||
|
|
n.traverse( (c) => !c.portal && (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.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 &&
|
||
|
|
!objs.find( (o) => o.uuid == n.uuid ) &&
|
||
|
|
(objs.push(n))
|
||
|
|
)
|
||
|
|
|
||
|
|
// put it into a scene (without .add() because it reparents objects) so we can render it separately
|
||
|
|
mesh.stencilObjects = new xrf.THREE.Scene()
|
||
|
|
mesh.stencilObjects.children = objs
|
||
|
|
|
||
|
|
// *TODO* stencilize any tagged plane without material
|
||
|
|
|
||
|
|
// enable the stencil-material of the stencil objects
|
||
|
|
const showPortal = (n,show) => {
|
||
|
|
if( n.portal ) n.visible = show
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
mesh.onAfterRender = function(renderer, scene, camera, geometry, material, group ){
|
||
|
|
mesh.stencilObjects.traverse( (n) => showPortal(n,false) && n.stencil && (n.stencil(true)) )
|
||
|
|
renderer.autoClear = false
|
||
|
|
renderer.autoClearStencil = false
|
||
|
|
//renderer.sortObjects = false
|
||
|
|
renderer.render( mesh.stencilObjects, camera )
|
||
|
|
//renderer.sortObjects = true
|
||
|
|
mesh.stencilObjects.traverse( (n) => showPortal(n,true) && n.stencil && (n.stencil(false)) )
|
||
|
|
}
|
||
|
|
|
||
|
|
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
|
||
|
|
console.log("enabling portal for object '${mesh.name}'`")
|
||
|
|
}
|
||
|
|
|
||
|
|
xrf.portalNonEuclidian.selectStencil = (n, stencilRef, depthTest) => {
|
||
|
|
if( n.material ){
|
||
|
|
n.material.stencilRef = stencilRef
|
||
|
|
n.material.stencilWrite = stencilRef
|
||
|
|
n.material.stencilFunc = xrf.THREE.EqualStencilFunc;
|
||
|
|
}
|
||
|
|
n.updateMatrixWorld(true)
|
||
|
|
}
|
||
|
|
|
||
|
|
xrf.portalNonEuclidian.stencilRef = 1
|