upend/webui/src/lib/components/display/blobs/ModelViewer.svelte

87 lines
2.3 KiB
Svelte

<script lang="ts">
import { createEventDispatcher, onMount } from "svelte";
const dispatch = createEventDispatcher();
export let src: string;
export let lookonly = false;
let root: HTMLElement;
onMount(async () => {
root.style.height = `${root.clientWidth}px`;
const THREE = await import("three");
const THREE_OC = await import("three/examples/jsm/controls/OrbitControls");
const THREE_STL = await import("three/examples/jsm/loaders/STLLoader");
const camera = new THREE.PerspectiveCamera(
70,
root.clientWidth / root.clientHeight,
);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(root.clientWidth, root.clientHeight);
root.appendChild(renderer.domElement);
const controls = new THREE_OC.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.1;
controls.enableZoom = true;
controls.autoRotate = true;
controls.autoRotateSpeed = 3;
const scene = new THREE.Scene();
scene.add(new THREE.HemisphereLight(0xffffff, 1.5));
const loader = new THREE_STL.STLLoader();
loader.load(src, (geometry) => {
const material = new THREE.MeshPhongMaterial({
color: 0xdc322f,
specular: 100,
shininess: 70,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const middle = new THREE.Vector3();
geometry.computeBoundingBox();
geometry.boundingBox.getCenter(middle);
mesh.geometry.applyMatrix4(
new THREE.Matrix4().makeTranslation(-middle.x, -middle.y, -middle.z),
);
mesh.geometry.applyMatrix4(
new THREE.Matrix4().makeRotationX(-Math.PI / 2),
);
const largestDimension = Math.max(
geometry.boundingBox.max.x,
geometry.boundingBox.max.y,
geometry.boundingBox.max.z,
);
camera.position.z = largestDimension * 2;
});
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
dispatch("loaded");
});
</script>
<div class="modelviewer" class:lookonly bind:this={root} />
<style>
.modelviewer {
width: 100%;
max-height: 100%;
}
.modelviewer.lookonly {
pointer-events: none;
}
</style>