mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 14:14:13 +01:00
Merge pull request #243 from yeicor-3d/feature/lighting-follow-camera
Main light can now be moved, intensity can be regulated
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"append": [
|
||||
"assets/fox.glb.license",
|
||||
"assets/qwantani_afternoon_1k.hdr.license",
|
||||
"assets/qwantani_afternoon_1k_hdr.jpg.license",
|
||||
"LICENSE"
|
||||
],
|
||||
"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,
|
||||
shadowIntensity: 0,
|
||||
// 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)
|
||||
skybox: (window.matchMedia("(prefers-color-scheme: dark)").matches ?
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEU4ODiyn42XAAAACklEQVQI" +
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {Renderer} from "@google/model-viewer/lib/three-components/Renderer"
|
||||
import type {Vector3} from "three";
|
||||
import {BufferGeometry, Mesh} from "three";
|
||||
import {acceleratedRaycast, computeBoundsTree, disposeBoundsTree} from 'three-mesh-bvh';
|
||||
import {setupLighting} from "./lighting.ts";
|
||||
|
||||
ModelViewerElement.modelCacheSize = 0; // Also needed to avoid tree shaking
|
||||
//@ts-ignore
|
||||
@@ -67,6 +68,7 @@ onUpdated(() => {
|
||||
});
|
||||
elem.value.addEventListener('camera-change', onCameraChange);
|
||||
elem.value.addEventListener('progress', (ev) => onProgress((ev as any).detail.totalProgress));
|
||||
setupLighting(elem.value);
|
||||
});
|
||||
|
||||
function onCameraChange() {
|
||||
@@ -213,11 +215,11 @@ watch(disableTap, (newDisableTap) => {
|
||||
<template>
|
||||
<!-- The main 3D model viewer -->
|
||||
<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"
|
||||
:poster="poster" :shadow-intensity="sett.shadowIntensity" :skybox-image="sett.skybox"
|
||||
: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%">
|
||||
<slot></slot>
|
||||
<!-- 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);
|
||||
|
||||
document.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);
|
||||
|
||||
document.addEventListener('mouseup', (event) => {
|
||||
panning = false;
|
||||
}, true);
|
||||
|
||||
modelViewer.addEventListener('touchend', (event) => {
|
||||
panning = false;
|
||||
}, true);
|
||||
}
|
||||
Reference in New Issue
Block a user