xrfragment/example/model-viewer/index.html

232 lines
7.9 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<title>&lt;model-viewer&gt; template</title>
<meta charset="utf-8" />
<meta name="description" content="&lt;model-viewer&gt; template" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body style="overflow:hidden; padding:10%">
<h1>&lt;model-viewer&gt; example</h1>
<div style="width:90%; height:70vh">
<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" camera-orbit="-8.142746deg 68.967deg 0.6179899m" camera-target="-0.003m 0.0722m 0.0391m" field-of-view="45deg" min-field-of-view="25deg" max-field-of-view="45deg" interpolation-decay="200" min-camera-orbit="auto auto 5%"
style="width:100%; height:100%; border-radius:5px; border:1px solid #CCC"
>
</model-viewer>
</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.165.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.165.0/examples/jsm/"
}
}
</script>
<script type="module">
import xrf from "./../../../dist/xrfragment.three.module.js";
import * as THREE from 'three'; // *TODO* get three handle from mv
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
import { USDZLoader } from 'three/addons/loaders/USDZLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
const mv = document.querySelector("model-viewer");
const $url = document.querySelector('#url')
const orbitDefault = '50deg 90deg 1.0m'
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 setupCSS = (scene) => {
if( document.querySelector('#viewbutton-css') ) return
let style = document.createElement('style')
style.type = 'text/css'
style.innerHTML = `
.view-button {
background: #fff;
border-radius: 4px;
border: none;
box-sizing: border-box;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
color: rgba(0, 0, 0, 0.8);
display: block;
font-family: Futura, Helvetica Neue, sans-serif;
font-size: 12px;
font-weight: 700;
max-width: 128px;
overflow-wrap: break-word;
padding: 0.5em 1em;
position: absolute;
width: max-content;
height: max-content;
transform: translate3d(-50%, -50%, 0);
}
`
document.body.appendChild(style)
}
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 )
mv.appendChild(btn)
}
const setupHotspots = (scene) => {
scene.traverse( (n) => {
if( n.userData.href ){
let pos = new THREE.Vector3()
n.getWorldPosition(pos)
createHotspot(pos, n)
}
})
}
const setupDefaultProjection = () => {
xrf.addEventListener('navigateLoaded', () => {
let frag = xrf.URI.parse( xrf.scene.children[0].userData['#'] )
if( frag.XRF.pos ){
let obj = xrf.scene.getObjectByName( frag.XRF.pos.string )
if( !obj ) return console.error('obj '+frag.XRF.pos.string+" not found")
const pos = obj.position
mv.cameraTarget = `${pos.x} ${pos.y} ${pos.z}`
mv.cameraOrbit = orbitDefault;
mv.fieldOfView = '45deg';
}
})
}
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.XRF && node.userData.XRF.href ){
node.userData.XRF.href.exec({type:'click'})
console.log("clicked!")
}
}
mv.querySelectorAll('button').forEach((hotspot) => {
hotspot.addEventListener('click', () => annotationClicked(hotspot));
});
}
const clear = (obj) => {
while(obj.children.length > 0){
clear(obj.children[0]);
obj.remove(obj.children[0]);
}
if(obj.geometry) obj.geometry.dispose();
if(obj.material){
//in case of map, bumpMap, normalMap, envMap ...
Object.keys(obj.material).forEach(prop => {
if(!obj.material[prop])
return;
if(obj.material[prop] !== null && typeof obj.material[prop].dispose === 'function')
obj.material[prop].dispose();
})
obj.material.dispose();
}
}
const opts = {
xrf,
THREE,
}
const onLoad = (opts) => function(){
const scene = mv[getSymbol('scene')]
const renderer = mv[getSymbol("renderer")].threeRenderer
const controls = mv[getSymbol("controls")]
const camera = mv[getSymbol("scene")].getCamera()
clear(scene);
opts = {
...opts,
scene,
renderer,
camera,
loaders: { gltf: GLTFLoader, glb: GLTFLoader, fbx: FBXLoader, obj: OBJLoader, usdz: USDZLoader },
controls
}
window.opts = opts
if( camera.parent == null ) scene.add(camera) // xr fragments expects in-scene camera
// enable XR fragments
let xrf = opts.xrf.init(opts)
window.xrf = xrf
//
xrf.addEventListener('href', (opts) => {
console.log("href!")
console.dir(opts)
})
//
let url = mv.src
setupDefaultProjection(xrf)
// $url.value = url
// mark current loaded scene for deletion by xrfragment library (except camera)
// now we re-insert the model via the XR Fragments lib (so it will parse the XRF metadata)
xrf.navigator.URI = xrf.URI.parse(document.location.href)
xrf.navigator.to(url)
//if( xrf.URI.isRelative( xrf.URI.parse(url) ) ){
// xrf.navigator.URI = xrf.URI.parse( xrf.navigator.URI.URN + url )
//}
//xrf.loadModel({...scene, scene},url,true)
//setupCSS()
//setupHotspots(scene)
//setupClicks()
console.log("ready")
}
mv.addEventListener("load", onLoad(opts) )
//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>