xrfragment/src/3rd/js/three/non-euclidian.js
2023-11-08 18:28:18 +01:00

74 lines
3.1 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 (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