fixed depth-issues by disabling depthtest of root-stencil objects
This commit is contained in:
parent
aff1f7e02e
commit
e5a2b3f4fb
9 changed files with 1199 additions and 815 deletions
File diff suppressed because one or more lines are too long
|
|
@ -74,10 +74,10 @@ xrf.reset = () => {
|
|||
return true
|
||||
};
|
||||
let nodes = []
|
||||
xrf.scene.traverse( (n) => n.audio ? n.audio.remove() : false )
|
||||
xrf.scene.traverse( (child) => child.isXRF ? nodes.push(child) : false )
|
||||
xrf.scene.traverse( (n) => n.audio && (n.audio.remove()) )
|
||||
xrf.scene.traverse( (child) => child.isXRF && (nodes.push(child)) )
|
||||
nodes.map( disposeObject ) // leave non-XRF objects intact
|
||||
xrf.interactive = xrf.InteractiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.interactive = xrf.interactiveGroup( xrf.THREE, xrf.renderer, xrf.camera)
|
||||
xrf.add( xrf.interactive )
|
||||
xrf.layers = 0
|
||||
xrf.emit('reset',{})
|
||||
|
|
|
|||
|
|
@ -1,74 +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.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
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// wrapper to survive in/outside modules
|
||||
|
||||
xrf.InteractiveGroup = function(THREE,renderer,camera){
|
||||
xrf.interactiveGroup = function(THREE,renderer,camera){
|
||||
|
||||
let {
|
||||
Group,
|
||||
|
|
@ -13,7 +13,7 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
|
|||
const _event = { type: '', data: _pointer };
|
||||
let object = {selected:false}
|
||||
|
||||
class InteractiveGroup extends Group {
|
||||
class interactive extends Group {
|
||||
|
||||
constructor( renderer, camera ) {
|
||||
|
||||
|
|
@ -71,9 +71,9 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
|
|||
element.addEventListener( 'pointerup', onPointerEvent );
|
||||
element.addEventListener( 'pointermove', onPointerEvent );
|
||||
element.addEventListener( 'mousedown', onPointerEvent );
|
||||
element.addEventListener( 'mouseup', onPointerEvent );
|
||||
element.addEventListener( 'mousemove', onPointerEvent );
|
||||
element.addEventListener( 'click', onPointerEvent );
|
||||
element.addEventListener( 'mouseup', onPointerEvent );
|
||||
|
||||
// WebXR Controller Events
|
||||
// TODO: Dispatch pointerevents too
|
||||
|
|
@ -138,5 +138,5 @@ xrf.InteractiveGroup = function(THREE,renderer,camera){
|
|||
|
||||
}
|
||||
|
||||
return new InteractiveGroup(renderer,camera)
|
||||
return new interactive(renderer,camera)
|
||||
}
|
||||
83
src/3rd/js/three/util/non-euclidian.js
Normal file
83
src/3rd/js/three/util/non-euclidian.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
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
|
||||
|
|
@ -3,12 +3,10 @@ xrf.addEventListener('dynamicKey', (opts) => {
|
|||
if( !scene ) return
|
||||
let remove = []
|
||||
// erase previous lines
|
||||
xrf.focusLine.lines.map( (line) => line.parent.remove(line) )
|
||||
xrf.focusLine.lines.map( (line) => line.parent && (line.parent.remove(line)) )
|
||||
xrf.focusLine.points = []
|
||||
xrf.focusLine.lines = []
|
||||
|
||||
//scene.traverse( (n) => n.selection ? remove.push(n) : false )
|
||||
//remove.map( (n) => scene.remove(n.selection) )
|
||||
// drawlines
|
||||
match.map( (w) => {
|
||||
w.nodes.map( (mesh) => xrf.drawLineToMesh({ ...opts, mesh}) )
|
||||
|
|
|
|||
|
|
@ -80,5 +80,5 @@ audioMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadAudio(mim
|
|||
// listen to t XR fragment changes
|
||||
xrf.addEventListener('t', (opts) => {
|
||||
let t = opts.frag.t
|
||||
xrf.scene.traverse( (n) => n.audio && n.audio.playXRF ? n.audio.playXRF(t) : false )
|
||||
xrf.scene.traverse( (n) => n.audio && n.audio.playXRF && (n.audio.playXRF(t)) )
|
||||
})
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ videoMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadVideo(mim
|
|||
// listen to t XR fragment changes
|
||||
xrf.addEventListener('t', (opts) => {
|
||||
let t = opts.frag.t
|
||||
xrf.scene.traverse( (n) => n.video ? n.video.playXRF(t) : false )
|
||||
xrf.scene.traverse( (n) => n.video && (n.video.playXRF(t)) )
|
||||
})
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ xrf.addEventListener('render', (opts) => {
|
|||
let {time} = opts
|
||||
if( !model ) return
|
||||
if( xrf.mixers.length ){
|
||||
xrf.mixers.map( (m) => m.isPlaying ? m.update( time ) : false )
|
||||
xrf.mixers.map( (m) => m.isPlaying && (m.update( time )) )
|
||||
|
||||
// update active camera in case selected by dynamicKey in URI
|
||||
if( xrf.model.camera && model.mixer.isPlaying ){
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue