Skip to content
Open
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
4 changes: 2 additions & 2 deletions packages/model-viewer/src/styles/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ const parseNumber = (() => {
* Parses a hexadecimal-encoded color in 3, 6 or 8 digit form.
*/
const parseHex = (() => {
// TODO(cdata): right now we don't actually enforce the number of digits
const HEX_RE = /^[a-f0-9]*/i;
const HEX_RE =
/^([a-f0-9]{8}|[a-f0-9]{6}|[a-f0-9]{4}|[a-f0-9]{3})(?![a-f0-9])/i;

return (inputString: string): ParseResult<HexNode> => {
inputString = inputString.slice(1).trim();
Expand Down
15 changes: 15 additions & 0 deletions packages/model-viewer/src/test/styles/parsers-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,25 @@ suite('parsers', () => {
test('parses hex colors', () => {
expect(parseExpressions('#fff')).to.be.eql([expressionNode(
[hexNode('fff')])]);
expect(parseExpressions('#abcd')).to.be.eql([expressionNode(
[hexNode('abcd')])]);
expect(parseExpressions('#abc123')).to.be.eql([expressionNode(
[hexNode('abc123')])]);
expect(parseExpressions('#daf012ee')).to.be.eql([expressionNode(
[hexNode('daf012ee')])]);

// Invalid hex digit counts should fail to parse
expect(parseExpressions('#')).to.be.eql([]);
expect(parseExpressions('#1')).to.be.eql([]);
expect(parseExpressions('#12')).to.be.eql([]);
expect(parseExpressions('#12345')).to.be.eql([]);
expect(parseExpressions('#1234567')).to.be.eql([]);
expect(parseExpressions('#123456789')).to.be.eql([]);
expect(parseExpressions('#123456fg')).to.be.eql([]);

// Correctly parses valid hex followed by non-hex characters
expect(parseExpressions('#123456g')).to.be.eql([expressionNode(
[hexNode('123456'), identNode('g')])]);
});

test('parses functions', () => {
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
Loading