From 0f46d06463329713be91881e0c0ec6e6c37312a3 Mon Sep 17 00:00:00 2001 From: Yeicor <4929005+Yeicor@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:10:08 +0100 Subject: [PATCH] add a couple of useful tools --- src/App.vue | 2 +- src/models/glb/merge.ts | 2 +- src/tools/Tools.vue | 81 ++++++++++++++++++++++++------- src/viewer/ModelViewerWrapper.vue | 7 +-- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/App.vue b/src/App.vue index 5f07b1b..55b03bf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -55,7 +55,7 @@ for (let model of settings.preloadModels) { - + diff --git a/src/models/glb/merge.ts b/src/models/glb/merge.ts index 7a86d99..787835b 100644 --- a/src/models/glb/merge.ts +++ b/src/models/glb/merge.ts @@ -15,7 +15,7 @@ export async function mergePartial(glb: Uint8Array, name: string, document: Docu let newDoc = await io.readBinary(glb); // noinspection TypeScriptValidateJSTypes - // await newDoc.transform(dropByName(name), setNames(name)); + await newDoc.transform(dropByName(name), setNames(name)); let merged = document.merge(newDoc); diff --git a/src/tools/Tools.vue b/src/tools/Tools.vue index 69c8bee..ebcf07a 100644 --- a/src/tools/Tools.vue +++ b/src/tools/Tools.vue @@ -3,15 +3,16 @@ import {VBtn} from "vuetify/lib/components"; import {ref} from "vue"; import OrientationGizmo from "./OrientationGizmo.vue"; import {OrthographicCamera} from "three/src/cameras/OrthographicCamera"; -import {mdiCrosshairsGps, mdiProjector} from '@mdi/js' +import {mdiCrosshairsGps, mdiCursorDefaultClick, mdiDownload, mdiProjector} from '@mdi/js' import SvgIcon from '@jamescoyle/vue-icon'; -import {SceneManagerData} from "../misc/scene"; -import type {ModelViewerElement} from '@google/model-viewer'; +import type {ModelViewerElement, RGBA} from '@google/model-viewer'; +import type {Material} from '@google/model-viewer/lib/features/scene-graph/material.js'; +import {SceneMgrRefData} from "../misc/scene"; -let props = defineProps<{ sceneMgrData: SceneManagerData }>(); +let props = defineProps<{ refSData: SceneMgrRefData }>(); function syncOrthoCamera(force: boolean) { - let scene = props.sceneMgrData.viewerScene; + let scene = props.refSData.viewerScene; if (!scene) return; let perspectiveCam = (scene as any).__perspectiveCamera; if (force || perspectiveCam && scene.camera != perspectiveCam) { @@ -28,7 +29,7 @@ function syncOrthoCamera(force: boolean) { let toggleProjectionText = ref('PERSP'); // Default to perspective camera function toggleProjection() { - let scene = props.sceneMgrData.viewerScene; + let scene = props.refSData.viewerScene; if (!scene) return; let prevCam = scene.camera; let wasPerspectiveCamera = prevCam.isPerspectiveCamera; @@ -44,32 +45,76 @@ function toggleProjection() { } function centerCamera() { - let viewer: ModelViewerElement = props.sceneMgrData.viewer; + let viewer: ModelViewerElement = props.refSData.viewer; if (!viewer) return; viewer.updateFraming(); } +let selectionEnabled = ref(false); +let prevHighlightedMaterial: Material | null = null; +let hasListener = false; +let selectionListener = (event: MouseEvent) => { + if (!selectionEnabled.value) return; + let viewer: ModelViewerElement = props.refSData.viewer; + const material = viewer.materialFromPoint(event.clientX, event.clientY); + if (material !== null && prevHighlightedMaterial?.index === material.index) return + if (prevHighlightedMaterial) { + prevHighlightedMaterial.pbrMetallicRoughness.setBaseColorFactor( + (prevHighlightedMaterial as any).__prevBaseColorFactor); + } + if (!material) { + prevHighlightedMaterial = null; + return; + } + (material as any).__prevBaseColorFactor = [...material.pbrMetallicRoughness.baseColorFactor]; + material.pbrMetallicRoughness.setBaseColorFactor([1, 0, 0, 1] as RGBA); + prevHighlightedMaterial = material; +}; + +function toggleSelection() { + let viewer: ModelViewerElement = props.refSData.viewer; + if (!viewer) return; + selectionEnabled.value = !selectionEnabled.value; + if (selectionEnabled.value) { + if (!hasListener) { + viewer.addEventListener('mousemove', selectionListener); + hasListener = true; + } + } else { + if (prevHighlightedMaterial) { + prevHighlightedMaterial.pbrMetallicRoughness.setBaseColorFactor( + (prevHighlightedMaterial as any).__prevBaseColorFactor); + prevHighlightedMaterial = null; + } + } +} + +async function downloadSceneGlb() { + let viewer = props.refSData.viewer; + if (!viewer) return; + const glTF = await viewer.exportScene(); + const file = new File([glTF], "export.glb"); + const link = document.createElement("a"); + link.download = file.name; + link.href = URL.createObjectURL(file); + link.click(); +} + diff --git a/src/viewer/ModelViewerWrapper.vue b/src/viewer/ModelViewerWrapper.vue index 36732e8..c59e329 100644 --- a/src/viewer/ModelViewerWrapper.vue +++ b/src/viewer/ModelViewerWrapper.vue @@ -1,13 +1,13 @@