Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
3 changes: 2 additions & 1 deletion packages/model-viewer/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions packages/model-viewer/src/features/loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,9 @@ export const LoadingMixin = <T extends Constructor<ModelViewerElementBase>>(
*
* 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;
Expand Down
74 changes: 37 additions & 37 deletions packages/model-viewer/src/test/three-components/ARRenderer-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
19 changes: 12 additions & 7 deletions packages/model-viewer/src/three-components/ARRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions packages/model-viewer/src/three-components/TextureUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>;
}
}



/**
Expand All @@ -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 = [];
Expand Down
Loading