166 lines
5.6 KiB
HTML
166 lines
5.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title><model-viewer> template</title>
|
|
<meta charset="utf-8" />
|
|
<meta name="description" content="<model-viewer> template" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
</head>
|
|
<body style="overflow:hidden; padding:10%">
|
|
|
|
<h1><model-viewer> example</h1>
|
|
<br><br>
|
|
|
|
<model-viewer
|
|
src="./../assets/index.glb"
|
|
environment-image="https://cdn.glitch.global/8e507517-31ff-4aa5-80c1-10ea6de9483d/white_furnace.hdr"
|
|
ar alt="XR Fragments demo scene"camera-controls touch-action="none" disable-tap
|
|
field-of-view="80deg" min-field-of-view="25deg" max-field-of-view="100deg"
|
|
interpolation-decay="200" camera-target="0m 0m 0m" min-camera-orbit="0.1% 0.1% 0.1%"
|
|
style="width:80%; height:50vh; border-radius:5px; border:1px solid #CCC"
|
|
>
|
|
</model-viewer>
|
|
|
|
<script src="./../../dist/xrfragment.js"></script>
|
|
<script>
|
|
const mv = window.mv = document.querySelector("model-viewer");
|
|
const $url = document.querySelector('#url')
|
|
const orbitDefault = '50deg 90deg 1.0m'
|
|
const panOffset = 1.6
|
|
|
|
function getSymbol(name) {
|
|
let obj = mv;
|
|
do {
|
|
const sym = Object.getOwnPropertySymbols(obj).find(
|
|
(x) => x.description === name
|
|
);
|
|
if (sym) {
|
|
return sym;
|
|
}
|
|
} while ((obj = Object.getPrototypeOf(obj)));
|
|
}
|
|
|
|
const createHotspot = (pos, n) => {
|
|
//.name, `${n.userData['aria-description'] || n.name}` )
|
|
const btn = document.createElement('button')
|
|
btn.innerText = n.name
|
|
btn.className = 'view-button'
|
|
btn.node = n
|
|
if( n.userData['aria-description']){
|
|
btn.alt = n.userData['aria-description']
|
|
btn.setAttribute("aria-description", n.userData['aria-description'])
|
|
}
|
|
btn.setAttribute('slot','hotspot-'+n.name)
|
|
btn.setAttribute('data-normal',"0 0 1")
|
|
btn.setAttribute('data-position',`${pos.x}m ${pos.y}m ${pos.z}m`)
|
|
btn.setAttribute('data-target',`${pos.x}m ${pos.y}m ${pos.z}m`)
|
|
btn.setAttribute('data-orbit', orbitDefault )
|
|
btn.style.cursor = 'pointer'
|
|
mv.appendChild(btn)
|
|
}
|
|
|
|
const setupHotspots = (scene) => {
|
|
scene.traverse( (n) => {
|
|
if( n.userData.href ){
|
|
let pos = n.position.clone()
|
|
n.getWorldPosition(pos)
|
|
createHotspot(pos, n)
|
|
}
|
|
})
|
|
setupClicks()
|
|
}
|
|
|
|
const setupClicks = () => {
|
|
// implement click action
|
|
const annotationClicked = (annotation) => {
|
|
let dataset = annotation.dataset;
|
|
let node = annotation.node
|
|
mv.cameraTarget = dataset.target;
|
|
mv.cameraOrbit = dataset.orbit;
|
|
mv.fieldOfView = '45deg';
|
|
console.dir(node)
|
|
if( node && node.userData.href ){
|
|
//node.userData.XRF.href.exec({type:'click'})
|
|
console.log("clicked!")
|
|
}
|
|
}
|
|
mv.querySelectorAll('button').forEach((hotspot) => {
|
|
hotspot.addEventListener('click', () => annotationClicked(hotspot));
|
|
});
|
|
}
|
|
|
|
const setupDefaultProjection = (scene) => {
|
|
let defaultStr = scene.userData['#']
|
|
if( !defaultStr ) return console.warn('no default # fragment found in 3D model')
|
|
console.log("defaultStr "+defaultStr)
|
|
let frag = xrf.URI.parse( defaultStr )
|
|
if( frag.XRF.pos ){
|
|
let obj = scene.getObjectByName( frag.XRF.pos.string )
|
|
if( !obj ) return console.error('obj '+frag.XRF.pos.string+" not found")
|
|
console.log("updating cam")
|
|
const pos = obj.position.clone()
|
|
obj.getWorldPosition(pos)
|
|
pos.y += panOffset
|
|
mv.cameraTarget = `${pos.x}m ${pos.y}m ${pos.z}m`
|
|
console.log(`${pos.x} ${pos.y} ${pos.z}`)
|
|
mv.cameraOrbit = orbitDefault;
|
|
mv.fieldOfView = '45deg';
|
|
}
|
|
}
|
|
|
|
const setupCSS = (scene) => {
|
|
if( document.querySelector('#viewbutton-css') ) return
|
|
let style = document.createElement('style')
|
|
style.type = 'text/css'
|
|
style.id = 'viewbutton-css'
|
|
style.innerHTML = `
|
|
.view-button{
|
|
opacity:0.001;
|
|
cursor:pointer;
|
|
}
|
|
.view-button:hover{
|
|
opacity:1;
|
|
cursor:pointer;
|
|
}
|
|
`
|
|
document.body.appendChild(style)
|
|
}
|
|
|
|
const onLoad = () => function(){
|
|
const scene = mv[getSymbol('scene')].getObjectByName('Target').children[0]
|
|
const renderer = mv[getSymbol("renderer")].threeRenderer
|
|
const controls = mv[getSymbol("controls")]
|
|
const camera = mv[getSymbol("scene")].getCamera()
|
|
const url = mv.src
|
|
|
|
opts = window.opts = {
|
|
scene,
|
|
renderer,
|
|
camera,
|
|
controls
|
|
}
|
|
|
|
window.opts = opts
|
|
window.xrf = window.xrfragment // shorten
|
|
|
|
setupCSS()
|
|
setupHotspots(scene)
|
|
setupDefaultProjection(scene)
|
|
|
|
// now we re-insert the model via the XR Fragments lib (so it will parse the XRF metadata)
|
|
}
|
|
|
|
mv.addEventListener("load", onLoad() )
|
|
//mv.addEventListener('before-render', function(){
|
|
// const scene = mv[getSymbol('scene')]
|
|
// scene.visible = false
|
|
// console.log("before-render")
|
|
//})
|
|
</script>
|
|
<!-- Loads <model-viewer> for browsers: -->
|
|
<script
|
|
type="module"
|
|
src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"
|
|
></script>
|
|
</body>
|
|
</html>
|