non-euclidian leaky objects bug solved
This commit is contained in:
parent
7249584dfa
commit
068a2da24b
5 changed files with 2581 additions and 1882 deletions
File diff suppressed because one or more lines are too long
|
|
@ -27,6 +27,7 @@ xrf.patchRenderer = function(opts){
|
||||||
// allow entities to do stuff during render (onBeforeRender and onAfterRender don't always fire)
|
// 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
|
xrf.emit('render',{scene,camera,time,render}) // allow fragments to do something at renderframe
|
||||||
render(scene,camera)
|
render(scene,camera)
|
||||||
|
xrf.emit('renderPost',{scene,camera,time,render,renderer}) // allow fragments to do something after renderframe
|
||||||
})(renderer.render.bind(renderer))
|
})(renderer.render.bind(renderer))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
xrf.portalNonEuclidian = function(opts){
|
|
||||||
let { frag, mesh, model, camera, scene, renderer, stencilObjects} = opts
|
|
||||||
|
|
||||||
// turn plane into stencilplane
|
|
||||||
mesh.material = new xrf.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
|
|
||||||
if( stencilObjects.length == 0 ) return console.warn(`no objects are tagged with (portal)object name '${mesh.name}'`)
|
|
||||||
stencilObjects = stencilObjects
|
|
||||||
.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.stencilObjects = new xrf.THREE.Scene()
|
|
||||||
mesh.stencilObjects.children = stencilObjects
|
|
||||||
|
|
||||||
// *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
|
|
||||||
|
|
||||||
// scan for non-euclidian portals (planes with nameless & textureless material which are tagged)
|
|
||||||
xrf.addEventListener('parseModel', (opts) => {
|
|
||||||
let {model} = opts
|
|
||||||
model.scene.traverse( (n) => {
|
|
||||||
const hasMaterialName = n.material && n.material.name.length > 0
|
|
||||||
const hasTexture = n.material && n.material.map
|
|
||||||
const isPlane = n.geometry && n.geometry.attributes.uv && n.geometry.attributes.uv.count == 4
|
|
||||||
const isHref = n.userData.href != undefined
|
|
||||||
const isSRC = n.userData.src != undefined || n.isSRC
|
|
||||||
let stencilObjects = XRWG.match(n.name,0)
|
|
||||||
const hasReferences = stencilObjects.length && stencilObjects[0].nodes.length > 1
|
|
||||||
|
|
||||||
if( n.geometry && hasReferences && !hasMaterialName && !hasTexture && !isSRC && !n.isSRC){
|
|
||||||
xrf.portalNonEuclidian({...opts, mesh:n, stencilObjects: stencilObjects[0].nodes})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
@ -4,11 +4,19 @@ xrf.frag.src = function(v, opts){
|
||||||
opts.embedded = v // indicate embedded XR fragment
|
opts.embedded = v // indicate embedded XR fragment
|
||||||
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
|
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
|
||||||
|
|
||||||
|
const hasMaterialName = mesh.material && mesh.material.name.length > 0
|
||||||
|
const hasTexture = mesh.material && mesh.material.map
|
||||||
|
const isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
||||||
|
const hasLocalSRC = mesh.userData.src != undefined && mesh.userData.src[0] == '#'
|
||||||
|
|
||||||
let src;
|
let src;
|
||||||
let url = v.string
|
let url = v.string
|
||||||
let vfrag = xrfragment.URI.parse(url)
|
let vfrag = xrfragment.URI.parse(url)
|
||||||
console.dir({url,vfrag})
|
|
||||||
opts.isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
|
// handle non-euclidian planes
|
||||||
|
if( mesh.geometry && !hasMaterialName && !hasTexture && hasLocalSRC && isPlane ){
|
||||||
|
return xrf.portalNonEuclidian(opts)
|
||||||
|
}
|
||||||
|
|
||||||
const addModel = (model,url,frag) => {
|
const addModel = (model,url,frag) => {
|
||||||
let scene = model.scene
|
let scene = model.scene
|
||||||
|
|
|
||||||
99
src/3rd/js/three/xrf/src/non-euclidian.js
Normal file
99
src/3rd/js/three/xrf/src/non-euclidian.js
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
xrf.portalNonEuclidian = function(opts){
|
||||||
|
let { frag, mesh, model, camera, scene, renderer} = opts
|
||||||
|
|
||||||
|
// turn plane into stencilplane
|
||||||
|
mesh.material = new xrf.THREE.MeshBasicMaterial({ color: 'white' });
|
||||||
|
mesh.material.depthWrite = true;
|
||||||
|
mesh.material.depthTest = false;
|
||||||
|
mesh.material.colorWrite = 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 = {
|
||||||
|
pos: mesh.position.clone(),
|
||||||
|
posWorld: new xrf.THREE.Vector3(),
|
||||||
|
stencilRef: xrf.portalNonEuclidian.stencilRef,
|
||||||
|
needUpdate: false
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.getWorldPosition(mesh.portal.posWorld)
|
||||||
|
|
||||||
|
// 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 ) => (sRef,newPos) => {
|
||||||
|
n.position.copy( sRef == 0 ? pos : newPos )
|
||||||
|
xrf.portalNonEuclidian.selectStencil(n, sRef )
|
||||||
|
n.updateMatrixWorld(true)
|
||||||
|
}
|
||||||
|
)( n.position.clone() )
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect related objects from XRWG to render inside stencilplane
|
||||||
|
let world = scene.getObjectByName( mesh.userData.src.substr(1) ) // strip #
|
||||||
|
if( !world ) return console.warn(`no objects were found (src:${mesh.userData.src}) for (portal)object name '${mesh.name}'`)
|
||||||
|
let stencilObjects = [mesh,world]
|
||||||
|
stencilObjects = stencilObjects
|
||||||
|
.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
|
||||||
|
|
||||||
|
// enable the stencil-material of the stencil objects to prevent stackoverflow (portal in portal rendering)
|
||||||
|
const showPortal = (n,show) => {
|
||||||
|
if( n.portal ) n.visible = show
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.onBeforeRender = function(renderer, scene, camera, geometry, material, group ){
|
||||||
|
mesh.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.onAfterRender = function(renderer, scene, camera, geometry, material, group ){
|
||||||
|
mesh.portal.needUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
xrf.addEventListener('renderPost', (opts) => {
|
||||||
|
if( mesh.portal && mesh.portal.needUpdate ){
|
||||||
|
let {scene,camera,time,render} = opts
|
||||||
|
let stencilRef = mesh.portal.stencilRef
|
||||||
|
let stencilPos = mesh.portal.posWorld
|
||||||
|
|
||||||
|
mesh.visible = true
|
||||||
|
|
||||||
|
mesh.portal.stencilObjects.traverse( (n) => showPortal(n,false) && n.stencil && n.stencil(stencilRef,stencilPos) )
|
||||||
|
renderer.autoClear = false
|
||||||
|
renderer.clearDepth()
|
||||||
|
render( mesh.portal.stencilObjects, camera )
|
||||||
|
mesh.portal.stencilObjects.traverse( (n) => showPortal(n,true) && n.stencil && (n.stencil(0)) )
|
||||||
|
|
||||||
|
mesh.portal.needUpdate = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
xrf.portalNonEuclidian.stencilRef += 1 // each portal has unique stencil id
|
||||||
|
console.log(`enabling portal for object '${mesh.name}' (stencilRef:${mesh.portal.stencilRef})`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
xrf.portalNonEuclidian.selectStencil = (n, stencilRef, nested) => {
|
||||||
|
if( n.material ){
|
||||||
|
n.material.stencilRef = stencilRef
|
||||||
|
n.material.stencilWrite = stencilRef > 0
|
||||||
|
n.material.stencilFunc = xrf.THREE.EqualStencilFunc;
|
||||||
|
}
|
||||||
|
if( n.children && !nested ) n.traverse( (m) => !m.portal && (xrf.portalNonEuclidian.selectStencil(m,stencilRef,true)) )
|
||||||
|
}
|
||||||
|
|
||||||
|
xrf.portalNonEuclidian.stencilRef = 1
|
||||||
Loading…
Add table
Reference in a new issue