mirror of
https://github.com/TomHodson/tomhodson.github.com.git
synced 2025-06-26 10:01:18 +02:00
port pointcloud to webcomponent
This commit is contained in:
parent
f3a97e5b04
commit
2e05be2a70
@ -2,7 +2,6 @@
|
|||||||
title: Replacing an image colour with transparency
|
title: Replacing an image colour with transparency
|
||||||
layout: post
|
layout: post
|
||||||
excerpt: What happens if you convert an RGB image to RGBA by pretending it was sitting on a white background?
|
excerpt: What happens if you convert an RGB image to RGBA by pretending it was sitting on a white background?
|
||||||
draft: true
|
|
||||||
|
|
||||||
images: /assets/blog/alpha_test
|
images: /assets/blog/alpha_test
|
||||||
thumbnail: /assets/blog/alpha_test/thumbnail.png
|
thumbnail: /assets/blog/alpha_test/thumbnail.png
|
||||||
|
@ -101,94 +101,75 @@ Click and drag to spin me around. It didn't really capture my nose very well, I
|
|||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img class="no-wc" src="{{page.assets}}/rear_stereo/point_cloud_preview.png">
|
<img class="no-wc" src="{{page.assets}}/rear_stereo/point_cloud_preview.png">
|
||||||
<canvas style ="width: 100%;" id="canvas-id-1"></canvas>
|
<point-cloud-viewer model="/assets/blog/heic_depth_map/rear_stereo/pointcloud.pcd" camera = '{"type":"perspective","position":[-3.598,-0.4154,1.971],"rotation":[0.2078,-1.06,0.1819],"zoom":1,"target":[0,0,0]}'>
|
||||||
|
</point-cloud-viewer>
|
||||||
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
|
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
|
||||||
|
<figcaption class="has-wc">An interactive point cloud view.</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import * as THREE from "three";
|
// import * as THREE from "three";
|
||||||
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
// import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
||||||
import { DragControls } from "three/addons/controls/DragControls.js";
|
// import { DragControls } from "three/addons/controls/DragControls.js";
|
||||||
import { PCDLoader } from 'three/addons/loaders/PCDLoader.js';
|
// import { PCDLoader } from 'three/addons/loaders/PCDLoader.js';
|
||||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
// import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
init('canvas-id-1', '{{page.assets}}/rear_stereo/pointcloud.pcd');
|
// init('canvas-id-1', '{{page.assets}}/rear_stereo/pointcloud.pcd');
|
||||||
init('canvas-id-2', '{{page.assets}}/front_facing/pointcloud.pcd');
|
// init('canvas-id-2', '{{page.assets}}/front_facing/pointcloud.pcd');
|
||||||
|
|
||||||
function init(canvas_id, url) {
|
// function init(canvas_id, url) {
|
||||||
let render, gui, orbitControls;
|
// let render, gui, orbitControls;
|
||||||
let canvas = document.getElementById(canvas_id);
|
// let canvas = document.getElementById(canvas_id);
|
||||||
const loader = new PCDLoader();
|
// const loader = new PCDLoader();
|
||||||
let scene = new THREE.Scene();
|
// let scene = new THREE.Scene();
|
||||||
scene.add( new THREE.AxesHelper( 1 ) );
|
// scene.add( new THREE.AxesHelper( 1 ) );
|
||||||
|
|
||||||
loader.load(url, function ( points ) {
|
// loader.load(url, function ( points ) {
|
||||||
points.geometry.center();
|
// points.geometry.center();
|
||||||
points.geometry.rotateZ( -Math.PI/2 );
|
// points.geometry.rotateZ( -Math.PI/2 );
|
||||||
points.name = 'depth_map';
|
// points.name = 'depth_map';
|
||||||
scene.add( points );
|
// scene.add( points );
|
||||||
points.material.color = new THREE.Color(0x999999);
|
// points.material.color = new THREE.Color(0x999999);
|
||||||
points.material.size = 0.001
|
// points.material.size = 0.001
|
||||||
render();
|
// render();
|
||||||
|
|
||||||
} );
|
// } );
|
||||||
|
|
||||||
// --- Scene ---
|
// // --- Scene ---
|
||||||
const aspect = canvas.clientWidth / canvas.clientHeight;
|
// const aspect = canvas.clientWidth / canvas.clientHeight;
|
||||||
let camera = new THREE.PerspectiveCamera( 30, aspect, 0.01, 40 );
|
// let camera = new THREE.PerspectiveCamera( 30, aspect, 0.01, 40 );
|
||||||
camera.position.set( -2, 2, 3);
|
// camera.position.set( -2, 2, 3);
|
||||||
camera.lookAt(0, 0, 0);
|
// camera.lookAt(0, 0, 0);
|
||||||
|
|
||||||
// --- Renderer (use the existing canvas) ---
|
// // --- Renderer (use the existing canvas) ---
|
||||||
let renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas, antialias: true });
|
// let renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas, antialias: true });
|
||||||
renderer.setSize(canvas.clientWidth, canvas.clientHeight,);
|
// renderer.setSize(canvas.clientWidth, canvas.clientHeight,);
|
||||||
|
|
||||||
render = () => renderer.render(scene, camera);
|
// render = () => renderer.render(scene, camera);
|
||||||
|
|
||||||
// --- OrbitControls ---
|
// // --- OrbitControls ---
|
||||||
orbitControls = new OrbitControls(camera, renderer.domElement);
|
// orbitControls = new OrbitControls(camera, renderer.domElement);
|
||||||
orbitControls.addEventListener( 'change', render);
|
// orbitControls.addEventListener( 'change', render);
|
||||||
|
|
||||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
|
// const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
|
||||||
scene.add(ambientLight);
|
// scene.add(ambientLight);
|
||||||
|
|
||||||
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
|
// const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
|
||||||
dirLight.position.set(5, 5, 10);
|
// dirLight.position.set(5, 5, 10);
|
||||||
scene.add(dirLight);
|
// scene.add(dirLight);
|
||||||
|
|
||||||
window.addEventListener('resize', onWindowResize, false);
|
// window.addEventListener('resize', onWindowResize, false);
|
||||||
|
|
||||||
function onWindowResize() {
|
// function onWindowResize() {
|
||||||
camera.aspect = canvas.clientWidth / canvas.clientHeight;
|
// camera.aspect = canvas.clientWidth / canvas.clientHeight;
|
||||||
camera.updateProjectionMatrix();
|
// camera.updateProjectionMatrix();
|
||||||
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
|
// renderer.setSize(canvas.clientWidth, canvas.clientHeight);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// const elem = document.querySelector('#screenshot');
|
// }
|
||||||
// elem.addEventListener('click', () => {
|
|
||||||
// render();
|
|
||||||
// canvas.toBlob((blob) => {
|
|
||||||
// saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const saveBlob = (function() {
|
|
||||||
// const a = document.createElement('a');
|
|
||||||
// document.body.appendChild(a);
|
|
||||||
// a.style.display = 'none';
|
|
||||||
// return function saveData(blob, fileName) {
|
|
||||||
// const url = window.URL.createObjectURL(blob);
|
|
||||||
// console.log(url);
|
|
||||||
// a.href = url;
|
|
||||||
// a.download = fileName;
|
|
||||||
// a.click();
|
|
||||||
// };
|
|
||||||
// }());
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
## Update
|
## Update
|
||||||
@ -207,6 +188,8 @@ The depth information, while lower resolution, is much better. My nose really po
|
|||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img class="no-wc" src="{{page.assets}}/front_facing/point_cloud_preview.png">
|
<img class="no-wc" src="{{page.assets}}/front_facing/point_cloud_preview.png">
|
||||||
<canvas style ="width: 100%;" id="canvas-id-2"></canvas>
|
<point-cloud-viewer model="/assets/blog/heic_depth_map/front_facing/pointcloud.pcd" camera = '{"type":"perspective","position":[-3.682,0.3606,1.82],"rotation":[-0.1955,-1.104,-0.1751],"zoom":1,"target":[0,0,0]}'>
|
||||||
|
</point-cloud-viewer>
|
||||||
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
|
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
|
||||||
|
<figcaption class="has-wc">An interactive point cloud view.</figcaption>
|
||||||
</figure>
|
</figure>
|
@ -4,7 +4,8 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
outline-model-viewer {
|
outline-model-viewer,
|
||||||
|
point-cloud-viewer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
85
assets/js/outline-model-viewer/PointCloudViewer.js
Normal file
85
assets/js/outline-model-viewer/PointCloudViewer.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import * as THREE from "three";
|
||||||
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
||||||
|
import { PCDLoader } from "three/addons/loaders/PCDLoader.js";
|
||||||
|
import { Timer } from "three/addons/Addons.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
componentHTML,
|
||||||
|
setupThreeJS,
|
||||||
|
deserialiseCamera,
|
||||||
|
deserialiseControls,
|
||||||
|
} from "./helpers.js";
|
||||||
|
|
||||||
|
export class PointCloudViewer extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.isVisible = true;
|
||||||
|
this.shadow = this.attachShadow({ mode: "open" });
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
const { container, canvas, scene, renderer, gui } = setupThreeJS(this);
|
||||||
|
|
||||||
|
const loader = new PCDLoader();
|
||||||
|
scene.add(new THREE.AxesHelper(1));
|
||||||
|
|
||||||
|
const model = this.getAttribute("model");
|
||||||
|
const render = () => renderer.render(scene, this.camera);
|
||||||
|
this.render = render;
|
||||||
|
|
||||||
|
loader.load(model, function (points) {
|
||||||
|
points.material.size = 0.05;
|
||||||
|
gui
|
||||||
|
.add(points.material, "size", 0.01, 0.2)
|
||||||
|
.name("Point Size")
|
||||||
|
.onChange(render);
|
||||||
|
|
||||||
|
points.geometry.center();
|
||||||
|
points.geometry.rotateZ(-Math.PI / 2);
|
||||||
|
points.name = "depth_map";
|
||||||
|
scene.add(points);
|
||||||
|
points.material.color = new THREE.Color(0x999999);
|
||||||
|
render();
|
||||||
|
console.log("Model Loaded.");
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- OrbitControls ---
|
||||||
|
this.controls = new OrbitControls(this.camera, renderer.domElement);
|
||||||
|
this.controls.addEventListener("change", render);
|
||||||
|
this.controls.enableDamping = true;
|
||||||
|
this.controls.dampingFactor = 0.25;
|
||||||
|
this.controls.autoRotate = true;
|
||||||
|
deserialiseControls(this);
|
||||||
|
|
||||||
|
canvas.addEventListener("click", () => {
|
||||||
|
this.controls.autoRotate = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
|
||||||
|
scene.add(ambientLight);
|
||||||
|
|
||||||
|
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
|
||||||
|
dirLight.position.set(5, 5, 10);
|
||||||
|
scene.add(dirLight);
|
||||||
|
|
||||||
|
window.addEventListener("resize", onWindowResize, false);
|
||||||
|
|
||||||
|
function onWindowResize() {
|
||||||
|
this.camera.aspect = canvas.clientWidth / canvas.clientHeight;
|
||||||
|
this.camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
|
||||||
|
}
|
||||||
|
const timer = new Timer();
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
if (this.isVisible) {
|
||||||
|
timer.update();
|
||||||
|
const delta = timer.getDelta();
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
this.controls.update(delta);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
210
assets/js/outline-model-viewer/helpers.js
Normal file
210
assets/js/outline-model-viewer/helpers.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import * as THREE from "three";
|
||||||
|
import GUI from "lil-gui";
|
||||||
|
|
||||||
|
export function serialiseCamera(component) {
|
||||||
|
const { camera, controls } = component;
|
||||||
|
const position = Object.values(camera.position);
|
||||||
|
const extractXYZ = ({ _x, _y, _z }) => [_x, _y, _z];
|
||||||
|
const rotation = extractXYZ(camera.rotation);
|
||||||
|
const fixed = (l) => l.map((x) => parseFloat(x.toPrecision(4)));
|
||||||
|
return JSON.stringify({
|
||||||
|
type: "perspective",
|
||||||
|
fov: camera.fov,
|
||||||
|
near: camera.near,
|
||||||
|
far: camera.far,
|
||||||
|
position: fixed(position),
|
||||||
|
rotation: fixed(rotation),
|
||||||
|
zoom: camera.zoom,
|
||||||
|
target: fixed(Object.values(controls.target)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo alllow isometric camera
|
||||||
|
export function deserialiseCamera(component) {
|
||||||
|
const { canvas, initial_camera_state } = component;
|
||||||
|
const aspect = canvas.clientWidth / canvas.clientHeight;
|
||||||
|
|
||||||
|
const camera = new THREE.PerspectiveCamera(30, aspect, 0.01, 40);
|
||||||
|
|
||||||
|
if (!initial_camera_state) return;
|
||||||
|
if (initial_camera_state.type !== "perspective") return;
|
||||||
|
if (initial_camera_state.fov) camera.fov = initial_camera_state.fov;
|
||||||
|
if (initial_camera_state.near) camera.near = initial_camera_state.near;
|
||||||
|
if (initial_camera_state.far) camera.far = initial_camera_state.far;
|
||||||
|
if (initial_camera_state.zoom) camera.zoom = initial_camera_state.zoom;
|
||||||
|
if (initial_camera_state.position)
|
||||||
|
camera.position.set(...initial_camera_state.position);
|
||||||
|
if (initial_camera_state.rotation)
|
||||||
|
camera.rotation.set(...initial_camera_state.rotation);
|
||||||
|
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserialiseControls(component) {
|
||||||
|
const { controls, initial_camera_state } = component;
|
||||||
|
if (initial_camera_state.target && controls.target)
|
||||||
|
controls.target.set(...initial_camera_state.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveBlob = (function () {
|
||||||
|
const a = document.createElement("a");
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.style.display = "none";
|
||||||
|
return function saveData(blob, fileName) {
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
console.log(url);
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
a.click();
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function takeScreenshot(component) {
|
||||||
|
const { canvas, render } = component;
|
||||||
|
render();
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentHTML(component_rect) {
|
||||||
|
const { height } = component_rect;
|
||||||
|
console.log("Height:", height);
|
||||||
|
return `
|
||||||
|
<div id="container">
|
||||||
|
<span id = "clicked-item"></span>
|
||||||
|
<!-- <button id="fullscreen-btn">⛶</button> --!>
|
||||||
|
<canvas class = "object-viewer"></canvas>
|
||||||
|
</div>
|
||||||
|
<link rel="stylesheet" href="/node_modules/lil-gui/dist/lil-gui.min.css">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clicked-item {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
font-size: 0.7em;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fullscreen-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
font-size: 24px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#fullscreen-btn:hover {
|
||||||
|
color: var(--theme-subtle-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lil-gui .title {height: 2em;}
|
||||||
|
.lil-gui.root {
|
||||||
|
margin-top: calc(${height}px - 2em);
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
--background-color: none;
|
||||||
|
--text-color: var(--theme-text-color);
|
||||||
|
--title-background-color: none;
|
||||||
|
--title-text-color: var(--theme-text-color);
|
||||||
|
--widget-color: var(--theme-subtle-outline);
|
||||||
|
--hover-color: lightgrey;
|
||||||
|
--focus-color: lightgrey;
|
||||||
|
--number-color: #2cc9ff;
|
||||||
|
--string-color: #a2db3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lil-gui button {
|
||||||
|
border: var(--theme-subtle-outline) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lil-gui .controller.string input {
|
||||||
|
background-color: var(--theme-subtle-outline);
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: ${height}px;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage const { container, canvas, scene, gui } = setupThreeJS(this);
|
||||||
|
function setupThreeJS(component) {
|
||||||
|
const component_rect = component.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Create the component HTML
|
||||||
|
component.shadow.innerHTML = componentHTML(component_rect);
|
||||||
|
component.container = component.shadow.querySelector("div#container");
|
||||||
|
component.canvas = component.shadow.querySelector("canvas");
|
||||||
|
const canvas_rect = component.canvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (component.getAttribute("camera")) {
|
||||||
|
component.initial_camera_state = JSON.parse(
|
||||||
|
component.getAttribute("camera")
|
||||||
|
);
|
||||||
|
component.removeAttribute("camera");
|
||||||
|
}
|
||||||
|
|
||||||
|
component.camera = deserialiseCamera(component);
|
||||||
|
|
||||||
|
component.scene = new THREE.Scene();
|
||||||
|
|
||||||
|
component.renderer = new THREE.WebGLRenderer({
|
||||||
|
canvas: component.canvas,
|
||||||
|
alpha: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
component.renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
component.renderer.setSize(canvas_rect.width, canvas_rect.height, false);
|
||||||
|
|
||||||
|
component.gui = new GUI({
|
||||||
|
title: "Settings",
|
||||||
|
container: component.container,
|
||||||
|
injectStyles: true,
|
||||||
|
closeFolders: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((component.getAttribute("debug") || "closed") !== "open")
|
||||||
|
component.gui.close();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
printCamera: () => console.log(serialiseCamera(component)),
|
||||||
|
screenshot: () => takeScreenshot(component),
|
||||||
|
resetCamera: () => {
|
||||||
|
deserialiseCamera(component);
|
||||||
|
component.render();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
component.gui.add(params, "printCamera").name("Print Viewport State");
|
||||||
|
component.gui.add(params, "screenshot").name("Take Screenshot");
|
||||||
|
component.gui.add(params, "resetCamera").name("Reset Viewport");
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { componentHTML, setupThreeJS };
|
@ -12,9 +12,14 @@ import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
|
|||||||
import { Timer } from "three/addons/Addons.js";
|
import { Timer } from "three/addons/Addons.js";
|
||||||
|
|
||||||
import GUI from "lil-gui";
|
import GUI from "lil-gui";
|
||||||
|
import { componentHTML, setupThreeJS, serialiseCamera } from "./helpers.js";
|
||||||
import { CustomOutlinePass } from "./CustomOutlinePass.js";
|
import { CustomOutlinePass } from "./CustomOutlinePass.js";
|
||||||
import FindSurfaces from "./FindSurfaces.js";
|
import FindSurfaces from "./FindSurfaces.js";
|
||||||
|
|
||||||
|
import { PointCloudViewer } from "./PointCloudViewer.js";
|
||||||
|
|
||||||
|
customElements.define("point-cloud-viewer", PointCloudViewer);
|
||||||
|
|
||||||
// Todo:
|
// Todo:
|
||||||
// Swap in the version of this code that has a debug GUI behind a flag
|
// Swap in the version of this code that has a debug GUI behind a flag
|
||||||
// Consider support for transparent objects by rendering them as a wireframe in the color and excluding them from the edge pass.
|
// Consider support for transparent objects by rendering them as a wireframe in the color and excluding them from the edge pass.
|
||||||
@ -59,26 +64,11 @@ function printGLTFScene(scene, maxDepth = 3, depth = 0, indent = 0) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const serialiseCamera = (camera, controls) => {
|
|
||||||
const position = Object.values(camera.position);
|
|
||||||
const extractXYZ = ({ _x, _y, _z }) => [_x, _y, _z];
|
|
||||||
const rotation = extractXYZ(camera.rotation);
|
|
||||||
const fixed = (l) => l.map((x) => parseFloat(x.toPrecision(4)));
|
|
||||||
return JSON.stringify({
|
|
||||||
position: fixed(position),
|
|
||||||
rotation: fixed(rotation),
|
|
||||||
zoom: camera.zoom,
|
|
||||||
target: fixed(Object.values(controls.target)),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export class OutlineModelViewer extends HTMLElement {
|
export class OutlineModelViewer extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.isVisible = true; // Track visibility
|
this.isVisible = true;
|
||||||
this.shadow = this.attachShadow({ mode: "open" });
|
this.shadow = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
// Mouse and raycaster
|
|
||||||
this.raycaster = new THREE.Raycaster();
|
this.raycaster = new THREE.Raycaster();
|
||||||
this.mouse = new THREE.Vector2();
|
this.mouse = new THREE.Vector2();
|
||||||
this.intersectedObject = null; // Store currently intersected object
|
this.intersectedObject = null; // Store currently intersected object
|
||||||
@ -95,8 +85,7 @@ export class OutlineModelViewer extends HTMLElement {
|
|||||||
const mul = 2;
|
const mul = 2;
|
||||||
|
|
||||||
let component_rect = this.getBoundingClientRect();
|
let component_rect = this.getBoundingClientRect();
|
||||||
|
this.shadow.innerHTML = componentHTML(component_rect);
|
||||||
this.render(component_rect.height);
|
|
||||||
|
|
||||||
const model_path = this.getAttribute("model");
|
const model_path = this.getAttribute("model");
|
||||||
const spin = (this.getAttribute("spin") || "true") === "true";
|
const spin = (this.getAttribute("spin") || "true") === "true";
|
||||||
@ -492,87 +481,6 @@ export class OutlineModelViewer extends HTMLElement {
|
|||||||
}
|
}
|
||||||
document.addEventListener("fullscreenchange", onFullScreenChange);
|
document.addEventListener("fullscreenchange", onFullScreenChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(height) {
|
|
||||||
this.shadow.innerHTML = `
|
|
||||||
<div id="container">
|
|
||||||
<span id = "clicked-item"></span>
|
|
||||||
<!-- <button id="fullscreen-btn">⛶</button> --!>
|
|
||||||
<canvas class = "object-viewer"></canvas>
|
|
||||||
</div>
|
|
||||||
<link rel="stylesheet" href="/node_modules/lil-gui/dist/lil-gui.min.css">
|
|
||||||
<style>
|
|
||||||
|
|
||||||
#container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clicked-item {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
z-index: 10;
|
|
||||||
font-size: 0.7em;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--theme-text-color);
|
|
||||||
opacity: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fullscreen-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
z-index: 10;
|
|
||||||
font-size: 24px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--theme-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#fullscreen-btn:hover {
|
|
||||||
color: var(--theme-subtle-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.lil-gui .title {height: 2em;}
|
|
||||||
.lil-gui.root {
|
|
||||||
margin-top: calc(${height}px - 2em);
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
--background-color: none;
|
|
||||||
--text-color: var(--theme-text-color);
|
|
||||||
--title-background-color: none;
|
|
||||||
--title-text-color: var(--theme-text-color);
|
|
||||||
--widget-color: var(--theme-subtle-outline);
|
|
||||||
--hover-color: lightgrey;
|
|
||||||
--focus-color: lightgrey;
|
|
||||||
--number-color: #2cc9ff;
|
|
||||||
--string-color: #a2db3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lil-gui button {
|
|
||||||
border: var(--theme-subtle-outline) 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lil-gui .controller.string input {
|
|
||||||
background-color: var(--theme-subtle-outline);
|
|
||||||
color: var(--theme-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: ${height}px;
|
|
||||||
border-radius: inherit;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("outline-model-viewer", OutlineModelViewer);
|
customElements.define("outline-model-viewer", OutlineModelViewer);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user