xrfragment-haxe/src/3rd/js/three/xrf/src.js

195 lines
7.4 KiB
JavaScript
Raw Normal View History

2023-06-07 17:42:21 +02:00
// *TODO* use webgl instancing
xrf.frag.src = function(v, opts){
opts.embedded = v // indicate embedded XR fragment
2023-10-12 17:04:46 +02:00
let { mesh, model, camera, scene, renderer, THREE, hashbus, frag} = opts
2023-07-04 15:23:10 +02:00
2024-02-29 09:57:20 +00:00
if( mesh.isSRC ) return // only embed src once
2024-07-12 18:08:18 +00:00
opts.isLocal = v.string[0] == '#'
opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
opts.isSRC = mesh.isSRC = true
2024-07-11 13:54:44 +00:00
// correct for relative urls
2024-07-12 18:08:18 +00:00
let url = v.string
2024-07-11 13:54:44 +00:00
if( v.string.charAt(0) != '#' && xrf.URI.isRelative( xrf.URI.parse( v.string ) ) ){
2024-07-12 18:08:18 +00:00
url = xrf.navigator.URI.URN + v.string
2024-07-11 13:54:44 +00:00
}
2024-07-12 18:08:18 +00:00
url = xrf.frag.src.expandURI( mesh, url )
2024-07-11 13:54:44 +00:00
2024-04-10 16:38:50 +00:00
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url).XRF
2024-07-11 13:54:44 +00:00
2023-11-14 18:08:19 +01:00
if(xrf.debug) console.log(`src.js: instancing ${opts.isLocal?'local':'remote'} object ${url}`)
2023-11-18 20:50:22 +01:00
if( opts.isLocal ){
2024-02-29 09:57:20 +00:00
xrf.frag.src.localSRC(url,srcFrag,opts) // local
2023-11-18 20:50:22 +01:00
}else xrf.frag.src.externalSRC(url,srcFrag,opts) // external file
2024-02-08 19:40:43 +01:00
xrf.hashbus.pub( url.replace(/.*#/,''), mesh) // eval src-url fragments
}
xrf.frag.src.expandURI = function(mesh,uri){
if( uri ) mesh.userData.srcTemplate = uri
mesh.userData.src = xrf.URI.template( mesh.userData.srcTemplate, xrf.URI.vars.__object )
return mesh.userData.src
2023-11-18 20:50:22 +01:00
}
2023-09-21 13:05:30 +02:00
2023-11-18 20:50:22 +01:00
xrf.frag.src.addModel = (model,url,frag,opts) => {
let {mesh} = opts
let scene = model.scene
2023-12-06 12:54:47 +01:00
scene = xrf.frag.src.filterScene(scene,{...opts,frag}) // get filtered scene
if( mesh.material && mesh.userData.src ) mesh.material.visible = false // hide placeholder object
2024-02-13 17:10:24 +00:00
if( opts.isPortal ){
2023-11-25 12:40:38 +01:00
xrf.portalNonEuclidian({...opts,model,scene:model.scene})
// only add external objects, because
// local scene-objects are already added to scene
if( !opts.isLocal ) xrf.add(scene)
2023-11-18 20:50:22 +01:00
}else{
xrf.frag.src.scale( scene, opts, url ) // scale scene
mesh.add(scene)
2023-06-07 17:42:21 +02:00
}
2024-06-12 08:51:42 +00:00
xrf.frag.src.enableSourcePortation({...opts, scene,mesh,url,model})
// flag everything isSRC & isXRF
mesh.isXRF = scene.isXRF = true
mesh.traverse( (n) => { n.isSRC = n.isXRF = n[ opts.isLocal ? 'isSRCLocal' : 'isSRCExternal' ] = true })
2024-02-29 09:57:20 +00:00
xrf.emit('parseModel', {...opts, isSRC:true, mesh, model}) // this will execute all embedded metadata/fragments e.g.
2023-11-18 20:50:22 +01:00
}
2023-07-04 15:23:10 +02:00
2023-11-18 20:50:22 +01:00
xrf.frag.src.renderAsPortal = (mesh) => {
// *TODO* should support better isFlat(mesh) check
2023-11-18 20:50:22 +01:00
const isPlane = mesh.geometry && mesh.geometry.attributes.uv && mesh.geometry.attributes.uv.count == 4
return xrf.hasNoMaterial(mesh) && isPlane
2023-11-18 20:50:22 +01:00
}
2023-10-12 17:04:46 +02:00
2024-02-29 09:57:20 +00:00
xrf.frag.src.enableSourcePortation = (opts) => {
2024-06-12 08:51:42 +00:00
let {scene,mesh,url,model,THREE} = opts
2024-02-29 09:57:20 +00:00
if( url[0] == '#' ) return
url = url.replace(/(&)?[-][\w-+\.]+(&)?/g,'&') // remove negative selectors to refer to original scene
2024-02-29 09:57:20 +00:00
if( !mesh.userData.href ){
// show sourceportation clickable sphere for non-portals
let scale = new THREE.Vector3()
let size = new THREE.Vector3()
scene.getWorldScale(scale)
new THREE.Box3().setFromObject(scene).getSize(size)
const geo = new THREE.SphereGeometry( Math.max(size.x, size.y, size.z) * scale.x * 0.33, 10, 10 )
2024-02-29 09:57:20 +00:00
const mat = new THREE.MeshBasicMaterial()
mat.visible = false // we just use this for collisions
const sphere = new THREE.Mesh( geo, mat )
sphere.isXRF = true
2024-02-29 09:57:20 +00:00
// reparent scene to sphere
let children = mesh.children
mesh.children = []
mesh.add(sphere)
children.map( (c) => sphere.add(c) )
// make sphere clickable/hoverable
let frag = {}
xrf.Parser.parse("href", url, frag)
sphere.userData = scene.userData // allow rich href notifications/hovers
sphere.userData.href = url.replace(/#.*/,'') // remove fragments to refer to original scene
2024-02-29 09:57:20 +00:00
sphere.userData.XRF = frag
xrf.hashbus.pub.fragment("href", {...opts, mesh:sphere, frag, skipXRWG:true, renderer:xrf.renderer, camera:xrf.camera })
}
for ( let i in scene.userData ) {
if( !mesh.userData[i] ) mesh.userData[i] = scene.userData[i] // allow rich href notifications/hovers
}
2023-11-18 20:50:22 +01:00
}
xrf.frag.src.externalSRC = (url,frag,opts) => {
fetch(url, { method: 'HEAD' })
.then( (res) => {
let mimetype = res.headers.get('Content-type')
if(xrf.debug != undefined ) console.log("HEAD "+url+" => "+mimetype)
2024-02-08 19:40:43 +01:00
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment'
if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment'
2023-11-18 20:50:22 +01:00
//if( url.match(/\.(fbx|stl|obj)$/) ) mimetype =
opts = { ...opts, frag, mimetype }
return xrf.frag.src.type[ mimetype ] ? xrf.frag.src.type[ mimetype ](url,opts) : xrf.frag.src.type.unknown(url,opts)
})
.then( (model) => {
if( model && model.scene ) xrf.frag.src.addModel(model, url, frag, opts )
})
.finally( () => { })
.catch( console.error )
return xrf.frag.src
}
2023-07-04 15:23:10 +02:00
2023-11-18 20:50:22 +01:00
xrf.frag.src.localSRC = (url,frag,opts) => {
2023-12-06 12:54:47 +01:00
let {model,mesh,scene} = opts
2024-02-13 17:10:24 +00:00
//setTimeout( (mesh,scene) => {
2023-12-06 12:54:47 +01:00
if( mesh.material ) mesh.material = mesh.material.clone() // clone, so we can individually highlight meshes
let _model = {
animations: model.animations,
2024-02-13 17:10:24 +00:00
scene: scene.clone()
2023-12-06 12:54:47 +01:00
}
_model.scene.isClone = true
_model.scene.traverse( (n) => n.isXRF = true ) // make sure they respond to xrf.reset()
2023-12-06 12:54:47 +01:00
_model.scenes = [_model.scene]
xrf.frag.src.addModel(_model,url,frag, opts) // current file
2024-02-13 17:10:24 +00:00
//},1000,mesh,scene )
2023-07-05 16:43:07 +02:00
}
// scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects
xrf.frag.src.scale = function(scene, opts, url){
let { mesh, model, camera, renderer, THREE} = opts
// remove invisible objects (hidden by selectors) which might corrupt boundingbox size-detection
let cleanScene = scene.clone()
let remove = []
const notVisible = (n) => !n.visible || (n.material && !n.material.visible)
cleanScene.traverse( (n) => notVisible(n) && n.children.length == 0 && (remove.push(n)) )
remove.map( (n) => n.removeFromParent() )
2023-09-21 13:05:30 +02:00
let restrictTo3DBoundingBox = mesh.geometry
if( restrictTo3DBoundingBox ){
2023-08-04 09:11:26 +02:00
// spec 3 of https://xrfragment.org/#src
// spec 1 of https://xrfragment.org/#scaling%20of%20instanced%20objects
// normalize instanced objectsize to boundingbox
2023-09-21 13:05:30 +02:00
let sizeFrom = new THREE.Vector3()
let sizeTo = new THREE.Vector3()
let empty = new THREE.Object3D()
new THREE.Box3().setFromObject(mesh).getSize(sizeTo)
new THREE.Box3().setFromObject(cleanScene).getSize(sizeFrom)
2023-09-21 13:05:30 +02:00
let ratio = sizeFrom.divide(sizeTo)
scene.scale.multiplyScalar( 1.0 / Math.max(ratio.x, ratio.y, ratio.z));
2023-08-04 09:11:26 +02:00
}else{
// spec 4 of https://xrfragment.org/#src
2023-08-08 12:40:38 +02:00
// spec 2 of https://xrfragment.org/#scaling%20of%20instanced%20objects
2023-08-04 09:11:26 +02:00
scene.scale.multiply( mesh.scale )
2023-07-05 16:43:07 +02:00
}
2023-09-21 13:05:30 +02:00
}
xrf.frag.src.filterScene = (scene,opts) => {
let { mesh, model, camera, renderer, THREE, hashbus, frag} = opts
2023-11-16 14:50:57 +01:00
2024-02-29 09:57:20 +00:00
scene = xrf.filter.scene({scene,frag,reparent:true}) //,copyScene: opts.isPortal})
2023-12-06 12:54:47 +01:00
if( !opts.isLocal ){
scene.traverse( (m) => {
if( m.userData && (m.userData.src || m.userData.href) ) return ; // prevent infinite recursion
2024-02-29 09:57:20 +00:00
xrf.parseModel.metadataInMesh(m,{scene,recursive:true})
2023-12-06 12:54:47 +01:00
})
}
2023-11-16 14:50:57 +01:00
return scene
2023-07-05 16:43:07 +02:00
}
2023-07-06 16:54:12 +02:00
/*
* replace the src-mesh with the contents of the src
*/
2023-07-05 16:43:07 +02:00
xrf.frag.src.type = {}
/*
* mimetype: unknown
*/
xrf.frag.src.type['unknown'] = function( url, opts ){
return new Promise( (resolve,reject) => {
reject(`${url} mimetype '${opts.mimetype}' not found or supported (yet)`)
2023-07-05 16:43:07 +02:00
})
}