mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 14:14:13 +01:00
Main light can now be moved, intensity can be regulated and update environment image for model viewer
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"append": [
|
"append": [
|
||||||
"assets/fox.glb.license",
|
"assets/fox.glb.license",
|
||||||
"assets/qwantani_afternoon_1k.hdr.license",
|
"assets/qwantani_afternoon_1k_hdr.jpg.license",
|
||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"replace": {
|
"replace": {
|
||||||
|
|||||||
Binary file not shown.
BIN
assets/qwantani_afternoon_1k_hdr.jpg
Normal file
BIN
assets/qwantani_afternoon_1k_hdr.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -30,7 +30,8 @@ export async function settings() {
|
|||||||
exposure: 1,
|
exposure: 1,
|
||||||
shadowIntensity: 0,
|
shadowIntensity: 0,
|
||||||
// Nice low-res outdoor/high-contrast HDRI image (CC0 licensed) for lighting
|
// Nice low-res outdoor/high-contrast HDRI image (CC0 licensed) for lighting
|
||||||
background: new URL('../../assets/qwantani_afternoon_1k.hdr', import.meta.url).href,
|
environment: new URL('../../assets/qwantani_afternoon_1k_hdr.jpg', import.meta.url).href,
|
||||||
|
environmentIntensity: 1.0,
|
||||||
// Uniform (1x1 pixel) medium gray background for visibility (following dark/light mode)
|
// Uniform (1x1 pixel) medium gray background for visibility (following dark/light mode)
|
||||||
skybox: (window.matchMedia("(prefers-color-scheme: dark)").matches ?
|
skybox: (window.matchMedia("(prefers-color-scheme: dark)").matches ?
|
||||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEU4ODiyn42XAAAACklEQVQI" +
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEU4ODiyn42XAAAACklEQVQI" +
|
||||||
@@ -38,6 +39,7 @@ export async function settings() {
|
|||||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEW6urpaLVq8AAAACklEQVQI" +
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEW6urpaLVq8AAAACklEQVQI" +
|
||||||
"12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="),
|
"12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="),
|
||||||
};
|
};
|
||||||
|
// settings.skybox = settings.background = ''; // Disable both skybox and background
|
||||||
|
|
||||||
// Auto-override any settings from the URL
|
// Auto-override any settings from the URL
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type {Renderer} from "@google/model-viewer/lib/three-components/Renderer"
|
|||||||
import type {Vector3} from "three";
|
import type {Vector3} from "three";
|
||||||
import {BufferGeometry, Mesh} from "three";
|
import {BufferGeometry, Mesh} from "three";
|
||||||
import {acceleratedRaycast, computeBoundsTree, disposeBoundsTree} from 'three-mesh-bvh';
|
import {acceleratedRaycast, computeBoundsTree, disposeBoundsTree} from 'three-mesh-bvh';
|
||||||
|
import {setupLighting} from "./lighting.ts";
|
||||||
|
|
||||||
ModelViewerElement.modelCacheSize = 0; // Also needed to avoid tree shaking
|
ModelViewerElement.modelCacheSize = 0; // Also needed to avoid tree shaking
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -67,6 +68,7 @@ onUpdated(() => {
|
|||||||
});
|
});
|
||||||
elem.value.addEventListener('camera-change', onCameraChange);
|
elem.value.addEventListener('camera-change', onCameraChange);
|
||||||
elem.value.addEventListener('progress', (ev) => onProgress((ev as any).detail.totalProgress));
|
elem.value.addEventListener('progress', (ev) => onProgress((ev as any).detail.totalProgress));
|
||||||
|
setupLighting(elem.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onCameraChange() {
|
function onCameraChange() {
|
||||||
@@ -213,11 +215,11 @@ watch(disableTap, (newDisableTap) => {
|
|||||||
<template>
|
<template>
|
||||||
<!-- The main 3D model viewer -->
|
<!-- The main 3D model viewer -->
|
||||||
<model-viewer ref="elem" v-if="sett != null" :ar="sett.arModes.length > 0" :ar-modes="sett.arModes"
|
<model-viewer ref="elem" v-if="sett != null" :ar="sett.arModes.length > 0" :ar-modes="sett.arModes"
|
||||||
:environment-image="sett.background" :exposure="sett.exposure" :autoplay="sett.autoplay"
|
:environment-image="sett.environment" :exposure="sett.exposure" :autoplay="sett.autoplay"
|
||||||
:orbit-sensitivity="sett.orbitSensitivity" :pan-sensitivity="sett.panSensitivity"
|
:orbit-sensitivity="sett.orbitSensitivity" :pan-sensitivity="sett.panSensitivity"
|
||||||
:poster="poster" :shadow-intensity="sett.shadowIntensity" :skybox-image="sett.skybox"
|
:poster="poster" :shadow-intensity="sett.shadowIntensity" :skybox-image="sett.skybox"
|
||||||
:src="props.src" :zoom-sensitivity="sett.zoomSensitivity" alt="The 3D model(s)" camera-controls
|
:src="props.src" :zoom-sensitivity="sett.zoomSensitivity" alt="The 3D model(s)" camera-controls
|
||||||
camera-orbit="30deg 75deg auto" interaction-prompt="none" max-camera-orbit="Infinity 180deg auto"
|
camera-orbit="45deg 45deg auto" interaction-prompt="none" max-camera-orbit="Infinity 180deg auto"
|
||||||
min-camera-orbit="-Infinity 0deg 5%" style="width: 100%; height: 100%">
|
min-camera-orbit="-Infinity 0deg 5%" style="width: 100%; height: 100%">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<!-- Add a progress bar to the top of the model viewer -->
|
<!-- Add a progress bar to the top of the model viewer -->
|
||||||
|
|||||||
75
frontend/viewer/lighting.ts
Normal file
75
frontend/viewer/lighting.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import {ModelViewerElement} from '@google/model-viewer';
|
||||||
|
import {$scene} from "@google/model-viewer/lib/model-viewer-base";
|
||||||
|
import {settings} from "../misc/settings.ts";
|
||||||
|
|
||||||
|
export async function setupLighting(modelViewer: ModelViewerElement) {
|
||||||
|
modelViewer[$scene].environmentIntensity = (await settings()).environmentIntensity;
|
||||||
|
// Code is mostly copied from the example at: https://modelviewer.dev/examples/stagingandcameras/#turnSkybox
|
||||||
|
let lastX: number;
|
||||||
|
let panning = false;
|
||||||
|
let skyboxAngle = 0;
|
||||||
|
let radiansPerPixel: number;
|
||||||
|
|
||||||
|
const startPan = () => {
|
||||||
|
const orbit = modelViewer.getCameraOrbit();
|
||||||
|
const {radius} = orbit;
|
||||||
|
radiansPerPixel = -1 * radius / modelViewer.getBoundingClientRect().height;
|
||||||
|
modelViewer.interactionPrompt = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatePan = (thisX: number) => {
|
||||||
|
const delta = (thisX - lastX) * radiansPerPixel;
|
||||||
|
lastX = thisX;
|
||||||
|
skyboxAngle += delta;
|
||||||
|
const orbit = modelViewer.getCameraOrbit();
|
||||||
|
orbit.theta += delta;
|
||||||
|
modelViewer.cameraOrbit = orbit.toString();
|
||||||
|
modelViewer.resetTurntableRotation(skyboxAngle);
|
||||||
|
modelViewer.jumpCameraToGoal();
|
||||||
|
}
|
||||||
|
|
||||||
|
modelViewer.addEventListener('mousedown', (event) => {
|
||||||
|
panning = event.metaKey || event.shiftKey;
|
||||||
|
if (!panning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastX = event.clientX;
|
||||||
|
startPan();
|
||||||
|
event.stopPropagation();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
modelViewer.addEventListener('touchstart', (event) => {
|
||||||
|
const {targetTouches, touches} = event;
|
||||||
|
panning = targetTouches.length === 2 && targetTouches.length === touches.length;
|
||||||
|
if (!panning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastX = 0.5 * (targetTouches[0].clientX + targetTouches[1].clientX);
|
||||||
|
startPan();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
self.addEventListener('mousemove', (event) => {
|
||||||
|
if (!panning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
updatePan(event.clientX);
|
||||||
|
event.stopPropagation();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
modelViewer.addEventListener('touchmove', (event) => {
|
||||||
|
if (!panning || event.targetTouches.length !== 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const {targetTouches} = event;
|
||||||
|
const thisX = 0.5 * (targetTouches[0].clientX + targetTouches[1].clientX);
|
||||||
|
updatePan(thisX);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
self.addEventListener('mouseup', (event) => {
|
||||||
|
panning = false;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
modelViewer.addEventListener('touchend', (event) => {
|
||||||
|
panning = false;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user