diff --git a/packages/model-viewer-effects/src/test/effect-composer-spec.ts b/packages/model-viewer-effects/src/test/effect-composer-spec.ts index 9e930a5ada..47c5e9bfe4 100644 --- a/packages/model-viewer-effects/src/test/effect-composer-spec.ts +++ b/packages/model-viewer-effects/src/test/effect-composer-spec.ts @@ -14,12 +14,11 @@ */ import {ModelViewerElement} from '@google/model-viewer'; +import {Renderer} from '@google/model-viewer/lib/three-components/Renderer.js'; import {expect} from 'chai'; import {DotScreenEffect, Effect, EffectPass, GridEffect} from 'postprocessing'; import {Camera} from 'three'; -import {Renderer} from '@google/model-viewer/lib/three-components/Renderer.js'; - import {$effectComposer, $normalPass, $renderPass, $scene} from '../effect-composer.js'; import {EffectComposer} from '../model-viewer-effects.js'; diff --git a/packages/model-viewer-effects/src/test/effects/color-grade-spec.ts b/packages/model-viewer-effects/src/test/effects/color-grade-spec.ts index 07a3773200..ecd03060f3 100644 --- a/packages/model-viewer-effects/src/test/effects/color-grade-spec.ts +++ b/packages/model-viewer-effects/src/test/effects/color-grade-spec.ts @@ -14,9 +14,8 @@ */ import {ModelViewerElement} from '@google/model-viewer'; -import {expect} from 'chai'; - import {Renderer} from '@google/model-viewer/lib/three-components/Renderer.js'; +import {expect} from 'chai'; import {ColorGradeEffect, EffectComposer} from '../../model-viewer-effects.js'; import {ArraysAreEqual, assetPath, AverageHSL, CompareArrays, createModelViewerElement, rafPasses, screenshot, waitForEvent} from '../utilities.js'; diff --git a/packages/model-viewer/src/constants.ts b/packages/model-viewer/src/constants.ts index ad6f36c49c..972fe17dee 100644 --- a/packages/model-viewer/src/constants.ts +++ b/packages/model-viewer/src/constants.ts @@ -67,7 +67,8 @@ export const IS_FIREFOX = /firefox/i.test(navigator.userAgent); export const IS_OCULUS = /OculusBrowser/.test(navigator.userAgent); export const IS_IOS_CHROME = IS_IOS && /CriOS\//.test(navigator.userAgent); export const IS_IOS_SAFARI = IS_IOS && IS_SAFARI; -export const IS_IOS_THIRDPARTY = IS_IOS && /CriOS\/|EdgiOS\/|FxiOS\/|GSA\/|DuckDuckGo\//.test(navigator.userAgent); +export const IS_IOS_THIRDPARTY = IS_IOS && + /CriOS\/|EdgiOS\/|FxiOS\/|GSA\/|DuckDuckGo\//.test(navigator.userAgent); export const IS_IOS_GSA = IS_IOS && /GSA\//.test(navigator.userAgent); export const IS_SCENEVIEWER_CANDIDATE = IS_ANDROID && !IS_FIREFOX && !IS_OCULUS; diff --git a/packages/model-viewer/src/features/loading.ts b/packages/model-viewer/src/features/loading.ts index 49d5874326..de86d4aeed 100644 --- a/packages/model-viewer/src/features/loading.ts +++ b/packages/model-viewer/src/features/loading.ts @@ -205,8 +205,9 @@ export const LoadingMixin = >( * * The default value is "auto". The only supported alternative values are * "lazy" and "eager". Auto is equivalent to lazy, which loads the model - * when it is near the viewport for reveal = "auto", and when dismissPoster() - * is called for reveal = "manual". Eager loads the model immediately. + * when it is near the viewport for reveal = "auto", and when + * dismissPoster() is called for reveal = "manual". Eager loads the model + * immediately. */ @property({type: String}) loading: LoadingAttributeValue = LoadingStrategy.AUTO; diff --git a/packages/model-viewer/src/test/three-components/ARRenderer-spec.ts b/packages/model-viewer/src/test/three-components/ARRenderer-spec.ts index cbbdcead7f..63450c7d8d 100644 --- a/packages/model-viewer/src/test/three-components/ARRenderer-spec.ts +++ b/packages/model-viewer/src/test/three-components/ARRenderer-spec.ts @@ -241,43 +241,43 @@ suite('ARRenderer', () => { expect(scale.z).to.be.equal(1); }); - test('prevents default WebXR select when beforexrselect is triggered on interactive elements', () => { - const button = document.createElement('button'); - const event = new CustomEvent('beforexrselect', { - bubbles: true, - cancelable: true - }); - Object.defineProperty(event, 'composedPath', { - value: () => [button, (arRenderer as any).overlay] - }); - - let defaultPrevented = false; - event.preventDefault = () => { - defaultPrevented = true; - }; - - (arRenderer as any).overlay.dispatchEvent(event); - expect(defaultPrevented).to.be.equal(true); - }); - - test('does not prevent default WebXR select on non-interactive elements', () => { - const div = document.createElement('div'); - const event = new CustomEvent('beforexrselect', { - bubbles: true, - cancelable: true - }); - Object.defineProperty(event, 'composedPath', { - value: () => [div, (arRenderer as any).overlay] - }); - - let defaultPrevented = false; - event.preventDefault = () => { - defaultPrevented = true; - }; - - (arRenderer as any).overlay.dispatchEvent(event); - expect(defaultPrevented).to.be.equal(false); - }); + test( + 'prevents default WebXR select when beforexrselect is triggered on interactive elements', + () => { + const button = document.createElement('button'); + const event = new CustomEvent( + 'beforexrselect', {bubbles: true, cancelable: true}); + Object.defineProperty(event, 'composedPath', { + value: () => [button, (arRenderer as any).overlay] + }); + + let defaultPrevented = false; + event.preventDefault = () => { + defaultPrevented = true; + }; + + (arRenderer as any).overlay.dispatchEvent(event); + expect(defaultPrevented).to.be.equal(true); + }); + + test( + 'does not prevent default WebXR select on non-interactive elements', + () => { + const div = document.createElement('div'); + const event = new CustomEvent( + 'beforexrselect', {bubbles: true, cancelable: true}); + Object.defineProperty(event, 'composedPath', { + value: () => [div, (arRenderer as any).overlay] + }); + + let defaultPrevented = false; + event.preventDefault = () => { + defaultPrevented = true; + }; + + (arRenderer as any).overlay.dispatchEvent(event); + expect(defaultPrevented).to.be.equal(false); + }); suite('presentation ends', () => { setup(async () => { diff --git a/packages/model-viewer/src/test/three-components/TextureUtils-spec.ts b/packages/model-viewer/src/test/three-components/TextureUtils-spec.ts index 52fcb6f5e0..e94d4d851d 100644 --- a/packages/model-viewer/src/test/three-components/TextureUtils-spec.ts +++ b/packages/model-viewer/src/test/three-components/TextureUtils-spec.ts @@ -73,24 +73,27 @@ suite('TextureUtils', () => { expect(texture.name).to.be.eq(EQUI_URL); expect(texture.mapping).to.be.eq(EquirectangularReflectionMapping); }); - test('decodes a gainmap and disposes intermediate render targets', async () => { - const GAINMAP_URL = assetPath('environments/spruit_sunrise_1k_HDR.jpg'); - const THREE = await import('three'); - let disposeCount = 0; - const originalDispose = THREE.WebGLRenderTarget.prototype.dispose; - THREE.WebGLRenderTarget.prototype.dispose = function() { - disposeCount++; - return originalDispose.call(this); - }; - - try { - const texture = await textureUtils.loadEquirect(GAINMAP_URL); - texture.dispose(); - expect(disposeCount).to.be.greaterThan(0); - } finally { - THREE.WebGLRenderTarget.prototype.dispose = originalDispose; - } - }); + test( + 'decodes a gainmap and disposes intermediate render targets', + async () => { + const GAINMAP_URL = + assetPath('environments/spruit_sunrise_1k_HDR.jpg'); + const THREE = await import('three'); + let disposeCount = 0; + const originalDispose = THREE.WebGLRenderTarget.prototype.dispose; + THREE.WebGLRenderTarget.prototype.dispose = function() { + disposeCount++; + return originalDispose.call(this); + }; + + try { + const texture = await textureUtils.loadEquirect(GAINMAP_URL); + texture.dispose(); + expect(disposeCount).to.be.greaterThan(0); + } finally { + THREE.WebGLRenderTarget.prototype.dispose = originalDispose; + } + }); test('loads a valid KTX2 texture from URL', async () => { let texture = await textureUtils.loadImage(KTX2_URL, false); texture.dispose(); diff --git a/packages/model-viewer/src/three-components/ARRenderer.ts b/packages/model-viewer/src/three-components/ARRenderer.ts index 0ed26085c8..18ee8c7b68 100644 --- a/packages/model-viewer/src/three-components/ARRenderer.ts +++ b/packages/model-viewer/src/three-components/ARRenderer.ts @@ -459,7 +459,8 @@ export class ARRenderer extends EventDispatcher< this.selectedXRController.userData.line.visible = false; if (scene.canScale && this.isWorldSpaceReady()) { this.isTwoHandInteraction = true; - this.firstRatio = this.controllerSeparation() / scene.scenePivot.scale.x; + this.firstRatio = + this.controllerSeparation() / scene.scenePivot.scale.x; this.scaleLine.visible = true; } } else { @@ -478,7 +479,8 @@ export class ARRenderer extends EventDispatcher< this.xrController2?.userData.isSelected) { if (scene.canScale && this.isWorldSpaceReady()) { this.isTwoHandInteraction = true; - this.firstRatio = this.controllerSeparation() / scene.scenePivot.scale.x; + this.firstRatio = + this.controllerSeparation() / scene.scenePivot.scale.x; this.scaleLine.visible = true; } } else { @@ -516,7 +518,8 @@ export class ARRenderer extends EventDispatcher< scene.attach(scene.scenePivot); this.selectedXRController = null; this.goalYaw = Math.atan2( - scene.scenePivot.matrix.elements[8], scene.scenePivot.matrix.elements[10]); + scene.scenePivot.matrix.elements[8], + scene.scenePivot.matrix.elements[10]); this.goalPosition.x = scene.scenePivot.position.x; this.goalPosition.z = scene.scenePivot.position.z; @@ -957,8 +960,8 @@ export class ARRenderer extends EventDispatcher< if (element instanceof HTMLElement) { const tagName = element.tagName.toLowerCase(); if (tagName === 'input' || tagName === 'button' || - tagName === 'select' || tagName === 'textarea' || - tagName === 'a' || element.hasAttribute('data-pointer-coalesce') || + tagName === 'select' || tagName === 'textarea' || tagName === 'a' || + element.hasAttribute('data-pointer-coalesce') || element.classList.contains('interactive')) { event.preventDefault(); break; @@ -1094,14 +1097,16 @@ export class ARRenderer extends EventDispatcher< } } - private applyXRControllerRotation(controller: XRController, scenePivot: Object3D) { + private applyXRControllerRotation( + controller: XRController, scenePivot: Object3D) { if (!controller.userData.turning) { return; } const angle = (controller.position.x - controller.userData.initialX) * ROTATION_SENSIVITY; this.deltaRotation.setFromAxisAngle(AXIS_Y, angle); - scenePivot.quaternion.multiplyQuaternions(this.deltaRotation, scenePivot.quaternion); + scenePivot.quaternion.multiplyQuaternions( + this.deltaRotation, scenePivot.quaternion); } private handleScalingInXR(scene: ModelScene, delta: number) { diff --git a/packages/model-viewer/src/three-components/TextureUtils.ts b/packages/model-viewer/src/three-components/TextureUtils.ts index f62e03bc8b..30aa5c8895 100644 --- a/packages/model-viewer/src/three-components/TextureUtils.ts +++ b/packages/model-viewer/src/three-components/TextureUtils.ts @@ -157,12 +157,15 @@ export default class TextureUtils { texture = dataTexture; result.dispose(true); } else { - console.warn('Decoded DataTexture is completely black, falling back to render target texture.'); + console.warn( + 'Decoded DataTexture is completely black, falling back to render target texture.'); texture = renderTarget.texture; result.dispose(false); } } catch (e) { - console.warn('Failed to convert gainmap to DataTexture, falling back to render target texture:', e); + console.warn( + 'Failed to convert gainmap to DataTexture, falling back to render target texture:', + e); texture = renderTarget.texture; result.dispose(false); } diff --git a/packages/model-viewer/src/three-components/gltf-instance/VariantMaterialExporterPlugin.ts b/packages/model-viewer/src/three-components/gltf-instance/VariantMaterialExporterPlugin.ts index 61a03113e3..0804508109 100644 --- a/packages/model-viewer/src/three-components/gltf-instance/VariantMaterialExporterPlugin.ts +++ b/packages/model-viewer/src/three-components/gltf-instance/VariantMaterialExporterPlugin.ts @@ -26,12 +26,19 @@ */ import {Material, Mesh, Object3D} from 'three'; -import {GLTFExporterPlugin} from 'three/examples/jsm/Addons.js'; +import {GLTFExporterPlugin, GLTFWriter} from 'three/examples/jsm/exporters/GLTFExporter.js'; import {VariantData} from '../../features/scene-graph/model.js'; import {UserDataVariantMapping} from './VariantMaterialLoaderPlugin.js'; +declare module 'three/examples/jsm/exporters/GLTFExporter.js' { + interface GLTFWriter { + json: any; + processMaterialAsync(material: Material): Promise; + } +} + /** @@ -57,19 +64,18 @@ const compatibleObject = (object: Object3D) => { * @param material {THREE.Material} * @return {boolean} */ -const compatibleMaterial = (material: Material|null) => { +const compatibleMaterial = (material: Material|null): material is Material => { // @TODO: support multi materials? - return material && material.isMaterial && !Array.isArray(material); + return !!(material && material.isMaterial && !Array.isArray(material)); }; export default class GLTFExporterMaterialsVariantsExtension implements GLTFExporterPlugin { - writer: any; // @TODO: Replace with GLTFWriter when GLTFExporter plugin TS - // declaration is ready + writer: GLTFWriter; name: string; variantNames: string[]; - constructor(writer: any) { + constructor(writer: GLTFWriter) { this.writer = writer; this.name = 'KHR_materials_variants'; this.variantNames = [];