New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@google/model-viewer

Package Overview
Dependencies
Maintainers
4
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@google/model-viewer - npm Package Compare versions

Comparing version 2.0.0-rc3 to 2.0.0

2

lib/features/ar.js

@@ -32,3 +32,3 @@ /* @license

const deserializeARModes = enumerationDeserializer(['quick-look', 'scene-viewer', 'webxr', 'none']);
const DEFAULT_AR_MODES = 'webxr scene-viewer';
const DEFAULT_AR_MODES = 'webxr scene-viewer quick-look';
const ARMode = {

@@ -35,0 +35,0 @@ QUICK_LOOK: 'quick-look',

@@ -22,3 +22,3 @@ /* @license

import { property } from 'lit/decorators.js';
import { $needsRender, $onModelLoad, $progressTracker, $renderer, $scene, $shouldAttemptPreload } from '../model-viewer-base.js';
import { $needsRender, $progressTracker, $renderer, $scene, $shouldAttemptPreload } from '../model-viewer-base.js';
import { clamp, deserializeUrl } from '../utilities.js';

@@ -33,5 +33,4 @@ export const BASE_OPACITY = 0.5;

const $cancelEnvironmentUpdate = Symbol('cancelEnvironmentUpdate');
const $onPreload = Symbol('onPreload');
export const EnvironmentMixin = (ModelViewerElement) => {
var _a, _b, _c, _d;
var _a, _b, _c;
class EnvironmentModelViewerElement extends ModelViewerElement {

@@ -48,16 +47,3 @@ constructor() {

this[_c] = null;
this[_d] = (event) => {
if (event.element === this) {
this[$updateEnvironment]();
}
};
}
connectedCallback() {
super.connectedCallback();
this[$renderer].loader.addEventListener('preload', this[$onPreload]);
}
disconnectedCallback() {
super.disconnectedCallback();
this[$renderer].loader.removeEventListener('preload', this[$onPreload]);
}
updated(changedProperties) {

@@ -86,7 +72,3 @@ super.updated(changedProperties);

}
[(_a = $currentEnvironmentMap, _b = $currentBackground, _c = $cancelEnvironmentUpdate, _d = $onPreload, $onModelLoad)]() {
super[$onModelLoad]();
this[$scene].setEnvironmentAndSkybox(this[$currentEnvironmentMap], this[$currentBackground]);
}
async [$updateEnvironment]() {
async [(_a = $currentEnvironmentMap, _b = $currentBackground, _c = $cancelEnvironmentUpdate, $updateEnvironment)]() {
const { skyboxImage, environmentImage } = this;

@@ -103,3 +85,3 @@ if (this[$cancelEnvironmentUpdate] != null) {

try {
const { environmentMap, skybox } = await textureUtils.generateEnvironmentMapAndSkybox(deserializeUrl(skyboxImage), environmentImage, (progress) => updateEnvProgress(clamp(progress, 0, 1) * 0.99));
const { environmentMap, skybox } = await textureUtils.generateEnvironmentMapAndSkybox(deserializeUrl(skyboxImage), environmentImage, (progress) => updateEnvProgress(clamp(progress, 0, 1)));
if (this[$currentEnvironmentMap] !== environmentMap) {

@@ -128,7 +110,3 @@ this[$currentEnvironmentMap] = environmentMap;

finally {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
updateEnvProgress(1.0);
});
});
updateEnvProgress(1.0);
}

@@ -135,0 +113,0 @@ }

@@ -162,4 +162,3 @@ import { ReactiveElement } from 'lit';

* Parses the element for an appropriate source URL and
* sets the views to use the new model based off of the `preload`
* attribute.
* sets the views to use the new model based.
*/

@@ -166,0 +165,0 @@ [$updateSource](): Promise<void>;

@@ -25,2 +25,3 @@ /* @license

import { HAS_INTERSECTION_OBSERVER, HAS_RESIZE_OBSERVER } from './constants.js';
import { $updateEnvironment } from './features/environment.js';
import { makeTemplate } from './template.js';

@@ -30,3 +31,3 @@ import { $evictionPolicy, CachingGLTFLoader } from './three-components/CachingGLTFLoader.js';

import { Renderer } from './three-components/Renderer.js';
import { clamp, debounce, timePasses } from './utilities.js';
import { clamp, debounce } from './utilities.js';
import { dataUrlToBlob } from './utilities/data-conversion.js';

@@ -164,9 +165,2 @@ import { ProgressTracker } from './utilities/progress-tracker.js';

new ModelScene({ canvas: this[$canvas], element: this, width, height });
this[$scene].addEventListener('model-load', async (event) => {
this[$markLoaded]();
this[$onModelLoad]();
// Give loading async tasks a chance to complete.
await timePasses();
this.dispatchEvent(new CustomEvent('load', { detail: { url: event.url } }));
});
// Update initial size on microtask timing so that subclasses have a

@@ -458,11 +452,12 @@ // chance to initialize

* Parses the element for an appropriate source URL and
* sets the views to use the new model based off of the `preload`
* attribute.
* sets the views to use the new model based.
*/
async [(_o = $onContextLost, $updateSource)]() {
if (this.loaded || !this[$shouldAttemptPreload]()) {
const scene = this[$scene];
if (this.loaded || !this[$shouldAttemptPreload]() ||
this.src === scene.url) {
return;
}
if (this.generateSchema) {
this[$scene].updateSchema(this.src);
scene.updateSchema(this.src);
}

@@ -474,9 +469,20 @@ this[$updateStatus]('Loading');

// throw exceptions and/or behave in unexpected ways:
this[$scene].stopAnimation();
scene.stopAnimation();
const updateSourceProgress = this[$progressTracker].beginActivity();
const source = this.src;
try {
await this[$scene].setSource(source, (progress) => updateSourceProgress(clamp(progress, 0, 1) * 0.95));
const detail = { url: source };
this.dispatchEvent(new CustomEvent('preload', { detail }));
const srcUpdated = scene.setSource(source, (progress) => updateSourceProgress(clamp(progress, 0, 1) * 0.95));
const envUpdated = this[$updateEnvironment]();
await Promise.all([srcUpdated, envUpdated]);
this[$markLoaded]();
this[$onModelLoad]();
// Wait for shaders to compile and pixels to be drawn.
await new Promise(resolve => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.dispatchEvent(new CustomEvent('load', { detail: { url: source } }));
resolve();
});
});
});
}

@@ -487,7 +493,3 @@ catch (error) {

finally {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
updateSourceProgress(1.0);
});
});
updateSourceProgress(1.0);
}

@@ -494,0 +496,0 @@ }

@@ -47,11 +47,6 @@ /* @license

let loadDispatched = false;
let preloadDispatched = false;
const loadHandler = () => {
loadDispatched = true;
};
const preloadHandler = () => {
preloadDispatched = true;
};
element.addEventListener('load', loadHandler);
element.addEventListener('preload', preloadHandler);
element.style.display = 'none';

@@ -64,5 +59,3 @@ // Give IntersectionObserver a chance to notify. In Chrome, this takes

element.removeEventListener('load', loadHandler);
element.removeEventListener('preload', preloadHandler);
expect(loadDispatched).to.be.false;
expect(preloadDispatched).to.be.false;
});

@@ -135,18 +128,12 @@ suite('load', () => {

suite('src changes quickly', () => {
test('eventually notifies that current src is preloaded', async () => {
test('eventually notifies that current src is loaded', async () => {
element.loading = 'eager';
element.src = CUBE_GLB_PATH;
const loadCubeEvent = waitForEvent(element, 'load');
await timePasses();
let preloadEvent = null;
const onPreload = (event) => {
if (event.detail.url === HORSE_GLB_PATH) {
preloadEvent = event;
}
};
element.addEventListener('preload', onPreload);
element.src = HORSE_GLB_PATH;
await until(() => element.loaded);
await timePasses();
element.removeEventListener('preload', onPreload);
expect(preloadEvent).to.be.ok;
const loadCube = await loadCubeEvent;
const loadHorse = await waitForEvent(element, 'load');
expect(loadCube.detail.url).to.be.eq(CUBE_GLB_PATH);
expect(loadHorse.detail.url).to.be.eq(HORSE_GLB_PATH);
});

@@ -153,0 +140,0 @@ });

@@ -37,10 +37,2 @@ /* @license

});
suite('setModelSource', () => {
test('fires a model-load event when loaded', async function () {
let fired = false;
scene.addEventListener('model-load', () => fired = true);
await scene.setSource(assetPath('models/Astronaut.glb'));
expect(fired).to.be.ok;
});
});
suite('with a model', () => {

@@ -47,0 +39,0 @@ setup(async () => {

@@ -222,3 +222,3 @@ /* @license

this.oldTarget.copy(scene.getTarget());
scene.addEventListener('model-load', this.onUpdateScene);
scene.element.addEventListener('load', this.onUpdateScene);
const radians = HIT_ANGLE_DEG * Math.PI / 180;

@@ -316,3 +316,3 @@ const ray = this.placeOnWall === true ?

scene.xrCamera = null;
scene.removeEventListener('model-load', this.onUpdateScene);
scene.element.removeEventListener('load', this.onUpdateScene);
scene.orientHotspots(0);

@@ -319,0 +319,0 @@ element.requestUpdate('cameraTarget');

@@ -150,3 +150,2 @@ /* @license

this.idealAspect = framingInfo.fieldOfViewAspect;
this.dispatchEvent({ type: 'model-load', url: this.url });
return;

@@ -205,3 +204,2 @@ }

this.setShadowIntensity(this.shadowIntensity);
this.dispatchEvent({ type: 'model-load', url: this.url });
}

@@ -208,0 +206,0 @@ reset() {

@@ -54,9 +54,2 @@ /// <reference types="webxr" />

constructor(options: RendererOptions);
/**
* Updates the renderer's size based on the largest scene and any changes to
* device pixel ratio.
*/
private updateRendererSize;
private updateRendererScale;
dispatchRenderScale(scene: ModelScene): void;
registerScene(scene: ModelScene): void;

@@ -72,2 +65,9 @@ unregisterScene(scene: ModelScene): void;

private countVisibleScenes;
/**
* Updates the renderer's size based on the largest scene and any changes to
* device pixel ratio.
*/
private updateRendererSize;
private updateRendererScale;
private shouldRender;
private rescaleCanvas;

@@ -74,0 +74,0 @@ private sceneSize;

@@ -26,5 +26,5 @@ /* @license

const DURATION_DECAY = 0.2;
const LOW_FRAME_DURATION_MS = 18;
const HIGH_FRAME_DURATION_MS = 26;
const MAX_AVG_CHANGE_MS = 2;
const LOW_FRAME_DURATION_MS = 40;
const HIGH_FRAME_DURATION_MS = 60;
const MAX_AVG_CHANGE_MS = 5;
const SCALE_STEPS = [1, 0.79, 0.62, 0.5, 0.4, 0.31, 0.25];

@@ -137,3 +137,59 @@ const DEFAULT_LAST_STEP = 3;

}
registerScene(scene) {
this.scenes.add(scene);
scene.forceRescale();
if (this.canRender && this.scenes.size > 0) {
this.threeRenderer.setAnimationLoop((time, frame) => this.render(time, frame));
}
if (this.debugger != null) {
this.debugger.addScene(scene);
}
}
unregisterScene(scene) {
this.scenes.delete(scene);
if (this.canvas3D.parentElement === scene.canvas.parentElement) {
scene.canvas.parentElement.removeChild(this.canvas3D);
}
if (this.canRender && this.scenes.size === 0) {
this.threeRenderer.setAnimationLoop(null);
}
if (this.debugger != null) {
this.debugger.removeScene(scene);
}
}
displayCanvas(scene) {
return this.multipleScenesVisible ? scene.element[$canvas] : this.canvas3D;
}
/**
* The function enables an optimization, where when there is only a single
* <model-viewer> element, we can use the renderer's 3D canvas directly for
* display. Otherwise we need to use the element's 2D canvas and copy the
* renderer's result into it.
*/
countVisibleScenes() {
const { canvas3D } = this;
let visibleScenes = 0;
let canvas3DScene = null;
for (const scene of this.scenes) {
const { element } = scene;
if (element.modelIsVisible && scene.externalRenderer == null) {
++visibleScenes;
}
if (canvas3D.parentElement === scene.canvas.parentElement) {
canvas3DScene = scene;
}
}
const multipleScenesVisible = visibleScenes > 1;
if (canvas3DScene != null) {
const newlyMultiple = multipleScenesVisible && !this.multipleScenesVisible;
const disappearing = !canvas3DScene.element.modelIsVisible;
if (newlyMultiple || disappearing) {
const { width, height } = this.sceneSize(canvas3DScene);
this.copyPixels(canvas3DScene, width, height);
canvas3D.parentElement.removeChild(canvas3D);
}
}
this.multipleScenesVisible = multipleScenesVisible;
}
/**
* Updates the renderer's size based on the largest scene and any changes to

@@ -196,4 +252,30 @@ * device pixel ratio.

}
dispatchRenderScale(scene) {
shouldRender(scene) {
if (!scene.shouldRender()) {
// The first frame we stop rendering the scene (because it stops moving),
// trigger one extra render at full scale.
if (scene.scaleStep != 0) {
scene.scaleStep = 0;
this.rescaleCanvas(scene);
}
else {
return false;
}
}
else if (scene.scaleStep != this.scaleStep) {
// Update render scale
scene.scaleStep = this.scaleStep;
this.rescaleCanvas(scene);
}
return true;
}
rescaleCanvas(scene) {
const scale = SCALE_STEPS[scene.scaleStep];
const width = Math.ceil(this.width / scale);
const height = Math.ceil(this.height / scale);
const { style } = scene.canvas;
style.width = `${width}px`;
style.height = `${height}px`;
this.canvas3D.style.width = `${width}px`;
this.canvas3D.style.height = `${height}px`;
const renderedDpr = this.dpr * scale;

@@ -214,93 +296,2 @@ const reason = scale < 1 ? 'GPU throttling' :

}
registerScene(scene) {
this.scenes.add(scene);
scene.forceRescale();
if (this.canRender && this.scenes.size > 0) {
this.threeRenderer.setAnimationLoop((time, frame) => this.render(time, frame));
}
if (this.debugger != null) {
this.debugger.addScene(scene);
}
}
unregisterScene(scene) {
this.scenes.delete(scene);
if (this.canvas3D.parentElement === scene.canvas.parentElement) {
scene.canvas.parentElement.removeChild(this.canvas3D);
}
if (this.canRender && this.scenes.size === 0) {
this.threeRenderer.setAnimationLoop(null);
}
if (this.debugger != null) {
this.debugger.removeScene(scene);
}
}
displayCanvas(scene) {
return this.multipleScenesVisible ? scene.element[$canvas] : this.canvas3D;
}
/**
* The function enables an optimization, where when there is only a single
* <model-viewer> element, we can use the renderer's 3D canvas directly for
* display. Otherwise we need to use the element's 2D canvas and copy the
* renderer's result into it.
*/
countVisibleScenes() {
const { canvas3D } = this;
let visibleScenes = 0;
let canvas3DScene = null;
for (const scene of this.scenes) {
const { element } = scene;
if (element.modelIsVisible && scene.externalRenderer == null) {
++visibleScenes;
}
if (canvas3D.parentElement === scene.canvas.parentElement) {
canvas3DScene = scene;
}
}
const multipleScenesVisible = visibleScenes > 1;
if (canvas3DScene != null) {
const newlyMultiple = multipleScenesVisible && !this.multipleScenesVisible;
const disappearing = !canvas3DScene.element.modelIsVisible;
if (newlyMultiple || disappearing) {
const { width, height } = this.sceneSize(canvas3DScene);
this.copyPixels(canvas3DScene, width, height);
canvas3D.parentElement.removeChild(canvas3D);
}
}
this.multipleScenesVisible = multipleScenesVisible;
}
rescaleCanvas(scene) {
const { style } = scene.canvas;
if (!scene.shouldRender()) {
// The first frame we stop rendering the scene (because it stops moving),
// trigger one extra render at full scale.
if (scene.scaleStep != 0) {
scene.scaleStep = 0;
style.width = `${this.width}px`;
style.height = `${this.height}px`;
this.dispatchRenderScale(scene);
if (!this.multipleScenesVisible) {
this.canvas3D.style.width = `${this.width}px`;
this.canvas3D.style.height = `${this.height}px`;
}
}
else {
return true; // Skip rendering
}
}
else if (scene.scaleStep != this.scaleStep) {
// Update render scale
scene.scaleStep = this.scaleStep;
const scale = this.scaleFactor;
const width = Math.ceil(this.width / scale);
const height = Math.ceil(this.height / scale);
style.width = `${width}px`;
style.height = `${height}px`;
if (!this.multipleScenesVisible) {
this.canvas3D.style.width = `${width}px`;
this.canvas3D.style.height = `${height}px`;
}
this.dispatchRenderScale(scene);
}
return false; // Perform rendering
}
sceneSize(scene) {

@@ -377,3 +368,3 @@ const { dpr } = this;

this.preRender(scene, t, delta);
if (this.rescaleCanvas(scene)) {
if (!this.shouldRender(scene)) {
continue;

@@ -380,0 +371,0 @@ }

{
"name": "@google/model-viewer",
"version": "2.0.0-rc3",
"version": "2.0.0",
"description": "Easily display interactive 3D models on the web and in AR!",

@@ -111,2 +111,2 @@ "repository": "https://github.com/google/model-viewer",

}
}
}

@@ -35,3 +35,3 @@ /* @license

const DEFAULT_AR_MODES = 'webxr scene-viewer';
const DEFAULT_AR_MODES = 'webxr scene-viewer quick-look';

@@ -38,0 +38,0 @@ const ARMode: {[index: string]: ARMode} = {

@@ -17,6 +17,5 @@ /* @license

import {property} from 'lit/decorators.js';
import {Event as ThreeEvent, Texture} from 'three';
import {Texture} from 'three';
import ModelViewerElementBase, {$needsRender, $onModelLoad, $progressTracker, $renderer, $scene, $shouldAttemptPreload} from '../model-viewer-base.js';
import {PreloadEvent} from '../three-components/CachingGLTFLoader.js';
import ModelViewerElementBase, {$needsRender, $progressTracker, $renderer, $scene, $shouldAttemptPreload} from '../model-viewer-base.js';
import {clamp, Constructor, deserializeUrl} from '../utilities.js';

@@ -33,3 +32,2 @@

const $cancelEnvironmentUpdate = Symbol('cancelEnvironmentUpdate');
const $onPreload = Symbol('onPreload');

@@ -70,18 +68,2 @@ export declare interface EnvironmentInterface {

private[$onPreload] = (event: ThreeEvent) => {
if ((event as PreloadEvent).element === this) {
this[$updateEnvironment]();
}
};
connectedCallback() {
super.connectedCallback();
this[$renderer].loader.addEventListener('preload', this[$onPreload]);
}
disconnectedCallback() {
super.disconnectedCallback();
this[$renderer].loader.removeEventListener('preload', this[$onPreload]);
}
updated(changedProperties: Map<string|number|symbol, unknown>) {

@@ -116,9 +98,2 @@ super.updated(changedProperties);

[$onModelLoad]() {
super[$onModelLoad]();
this[$scene].setEnvironmentAndSkybox(
this[$currentEnvironmentMap], this[$currentBackground]);
}
async[$updateEnvironment]() {

@@ -145,4 +120,3 @@ const {skyboxImage, environmentImage} = this;

environmentImage,
(progress: number) =>
updateEnvProgress(clamp(progress, 0, 1) * 0.99));
(progress: number) => updateEnvProgress(clamp(progress, 0, 1)));

@@ -171,7 +145,3 @@ if (this[$currentEnvironmentMap] !== environmentMap) {

} finally {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
updateEnvProgress(1.0);
});
});
updateEnvProgress(1.0);
}

@@ -178,0 +148,0 @@ }

@@ -21,2 +21,3 @@ /* @license

import {HAS_INTERSECTION_OBSERVER, HAS_RESIZE_OBSERVER} from './constants.js';
import {$updateEnvironment} from './features/environment.js';
import {makeTemplate} from './template.js';

@@ -26,3 +27,3 @@ import {$evictionPolicy, CachingGLTFLoader} from './three-components/CachingGLTFLoader.js';

import {ContextLostEvent, Renderer} from './three-components/Renderer.js';
import {clamp, debounce, timePasses} from './utilities.js';
import {clamp, debounce} from './utilities.js';
import {dataUrlToBlob} from './utilities/data-conversion.js';

@@ -262,13 +263,2 @@ import {ProgressTracker} from './utilities/progress-tracker.js';

this[$scene].addEventListener('model-load', async (event) => {
this[$markLoaded]();
this[$onModelLoad]();
// Give loading async tasks a chance to complete.
await timePasses();
this.dispatchEvent(
new CustomEvent('load', {detail: {url: (event as any).url}}));
});
// Update initial size on microtask timing so that subclasses have a

@@ -585,7 +575,8 @@ // chance to initialize

* Parses the element for an appropriate source URL and
* sets the views to use the new model based off of the `preload`
* attribute.
* sets the views to use the new model based.
*/
async[$updateSource]() {
if (this.loaded || !this[$shouldAttemptPreload]()) {
const scene = this[$scene];
if (this.loaded || !this[$shouldAttemptPreload]() ||
this.src === scene.url) {
return;

@@ -595,3 +586,3 @@ }

if (this.generateSchema) {
this[$scene].updateSchema(this.src);
scene.updateSchema(this.src);
}

@@ -603,3 +594,3 @@ this[$updateStatus]('Loading');

// throw exceptions and/or behave in unexpected ways:
this[$scene].stopAnimation();
scene.stopAnimation();

@@ -609,3 +600,3 @@ const updateSourceProgress = this[$progressTracker].beginActivity();

try {
await this[$scene].setSource(
const srcUpdated = scene.setSource(
source,

@@ -615,4 +606,19 @@ (progress: number) =>

const detail = {url: source};
this.dispatchEvent(new CustomEvent('preload', {detail}));
const envUpdated = (this as any)[$updateEnvironment]();
await Promise.all([srcUpdated, envUpdated]);
this[$markLoaded]();
this[$onModelLoad]();
// Wait for shaders to compile and pixels to be drawn.
await new Promise<void>(resolve => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.dispatchEvent(
new CustomEvent('load', {detail: {url: source}}));
resolve();
});
});
});
} catch (error) {

@@ -622,9 +628,5 @@ this.dispatchEvent(new CustomEvent(

} finally {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
updateSourceProgress(1.0);
});
});
updateSourceProgress(1.0);
}
}
}

@@ -56,12 +56,7 @@ /* @license

let loadDispatched = false;
let preloadDispatched = false;
const loadHandler = () => {
loadDispatched = true;
};
const preloadHandler = () => {
preloadDispatched = true;
};
element.addEventListener('load', loadHandler);
element.addEventListener('preload', preloadHandler);

@@ -79,6 +74,4 @@ element.style.display = 'none';

element.removeEventListener('load', loadHandler);
element.removeEventListener('preload', preloadHandler);
expect(loadDispatched).to.be.false;
expect(preloadDispatched).to.be.false;
});

@@ -167,25 +160,18 @@

suite('src changes quickly', () => {
test('eventually notifies that current src is preloaded', async () => {
test('eventually notifies that current src is loaded', async () => {
element.loading = 'eager';
element.src = CUBE_GLB_PATH;
const loadCubeEvent =
waitForEvent(element, 'load') as Promise<CustomEvent>;
await timePasses();
let preloadEvent = null;
const onPreload = (event: CustomEvent) => {
if (event.detail.url === HORSE_GLB_PATH) {
preloadEvent = event;
}
};
element.addEventListener<any>('preload', onPreload);
element.src = HORSE_GLB_PATH;
await until(() => element.loaded);
const loadCube = await loadCubeEvent;
const loadHorse = await waitForEvent(element, 'load') as CustomEvent;
await timePasses();
element.removeEventListener<any>('preload', onPreload);
expect(preloadEvent).to.be.ok;
expect(loadCube.detail.url).to.be.eq(CUBE_GLB_PATH);
expect(loadHorse.detail.url).to.be.eq(HORSE_GLB_PATH);
});

@@ -192,0 +178,0 @@ });

@@ -47,11 +47,2 @@ /* @license

suite('setModelSource', () => {
test('fires a model-load event when loaded', async function() {
let fired = false;
scene.addEventListener('model-load', () => fired = true);
await scene.setSource(assetPath('models/Astronaut.glb'));
expect(fired).to.be.ok;
});
});
suite('with a model', () => {

@@ -58,0 +49,0 @@ setup(async () => {

@@ -234,3 +234,3 @@ /* @license

scene.addEventListener('model-load', this.onUpdateScene);
scene.element.addEventListener('load', this.onUpdateScene);

@@ -354,3 +354,3 @@ const radians = HIT_ANGLE_DEG * Math.PI / 180;

scene.removeEventListener('model-load', this.onUpdateScene);
scene.element.removeEventListener('load', this.onUpdateScene);
scene.orientHotspots(0);

@@ -357,0 +357,0 @@ element.requestUpdate('cameraTarget');

@@ -207,4 +207,2 @@ /* @license

this.idealAspect = framingInfo.fieldOfViewAspect;
this.dispatchEvent({type: 'model-load', url: this.url});
return;

@@ -276,3 +274,2 @@ }

this.setShadowIntensity(this.shadowIntensity);
this.dispatchEvent({type: 'model-load', url: this.url});
}

@@ -279,0 +276,0 @@

@@ -42,5 +42,5 @@ /* @license

const DURATION_DECAY = 0.2;
const LOW_FRAME_DURATION_MS = 18;
const HIGH_FRAME_DURATION_MS = 26;
const MAX_AVG_CHANGE_MS = 2;
const LOW_FRAME_DURATION_MS = 40;
const HIGH_FRAME_DURATION_MS = 60;
const MAX_AVG_CHANGE_MS = 5;
const SCALE_STEPS = [1, 0.79, 0.62, 0.5, 0.4, 0.31, 0.25];

@@ -176,3 +176,72 @@ const DEFAULT_LAST_STEP = 3;

registerScene(scene: ModelScene) {
this.scenes.add(scene);
scene.forceRescale();
if (this.canRender && this.scenes.size > 0) {
this.threeRenderer.setAnimationLoop(
(time: number, frame?: any) => this.render(time, frame));
}
if (this.debugger != null) {
this.debugger.addScene(scene);
}
}
unregisterScene(scene: ModelScene) {
this.scenes.delete(scene);
if (this.canvas3D.parentElement === scene.canvas.parentElement) {
scene.canvas.parentElement!.removeChild(this.canvas3D);
}
if (this.canRender && this.scenes.size === 0) {
this.threeRenderer.setAnimationLoop(null);
}
if (this.debugger != null) {
this.debugger.removeScene(scene);
}
}
displayCanvas(scene: ModelScene): HTMLCanvasElement {
return this.multipleScenesVisible ? scene.element[$canvas] : this.canvas3D;
}
/**
* The function enables an optimization, where when there is only a single
* <model-viewer> element, we can use the renderer's 3D canvas directly for
* display. Otherwise we need to use the element's 2D canvas and copy the
* renderer's result into it.
*/
private countVisibleScenes() {
const {canvas3D} = this;
let visibleScenes = 0;
let canvas3DScene = null;
for (const scene of this.scenes) {
const {element} = scene;
if (element.modelIsVisible && scene.externalRenderer == null) {
++visibleScenes;
}
if (canvas3D.parentElement === scene.canvas.parentElement) {
canvas3DScene = scene;
}
}
const multipleScenesVisible = visibleScenes > 1;
if (canvas3DScene != null) {
const newlyMultiple =
multipleScenesVisible && !this.multipleScenesVisible;
const disappearing = !canvas3DScene.element.modelIsVisible;
if (newlyMultiple || disappearing) {
const {width, height} = this.sceneSize(canvas3DScene);
this.copyPixels(canvas3DScene, width, height);
canvas3D.parentElement!.removeChild(canvas3D);
}
}
this.multipleScenesVisible = multipleScenesVisible;
}
/**
* Updates the renderer's size based on the largest scene and any changes to

@@ -247,4 +316,31 @@ * device pixel ratio.

dispatchRenderScale(scene: ModelScene) {
private shouldRender(scene: ModelScene): boolean {
if (!scene.shouldRender()) {
// The first frame we stop rendering the scene (because it stops moving),
// trigger one extra render at full scale.
if (scene.scaleStep != 0) {
scene.scaleStep = 0;
this.rescaleCanvas(scene);
} else {
return false;
}
} else if (scene.scaleStep != this.scaleStep) {
// Update render scale
scene.scaleStep = this.scaleStep;
this.rescaleCanvas(scene);
}
return true;
}
private rescaleCanvas(scene: ModelScene) {
const scale = SCALE_STEPS[scene.scaleStep];
const width = Math.ceil(this.width / scale);
const height = Math.ceil(this.height / scale);
const {style} = scene.canvas;
style.width = `${width}px`;
style.height = `${height}px`;
this.canvas3D.style.width = `${width}px`;
this.canvas3D.style.height = `${height}px`;
const renderedDpr = this.dpr * scale;

@@ -266,107 +362,2 @@ const reason = scale < 1 ? 'GPU throttling' :

registerScene(scene: ModelScene) {
this.scenes.add(scene);
scene.forceRescale();
if (this.canRender && this.scenes.size > 0) {
this.threeRenderer.setAnimationLoop(
(time: number, frame?: any) => this.render(time, frame));
}
if (this.debugger != null) {
this.debugger.addScene(scene);
}
}
unregisterScene(scene: ModelScene) {
this.scenes.delete(scene);
if (this.canvas3D.parentElement === scene.canvas.parentElement) {
scene.canvas.parentElement!.removeChild(this.canvas3D);
}
if (this.canRender && this.scenes.size === 0) {
this.threeRenderer.setAnimationLoop(null);
}
if (this.debugger != null) {
this.debugger.removeScene(scene);
}
}
displayCanvas(scene: ModelScene): HTMLCanvasElement {
return this.multipleScenesVisible ? scene.element[$canvas] : this.canvas3D;
}
/**
* The function enables an optimization, where when there is only a single
* <model-viewer> element, we can use the renderer's 3D canvas directly for
* display. Otherwise we need to use the element's 2D canvas and copy the
* renderer's result into it.
*/
private countVisibleScenes() {
const {canvas3D} = this;
let visibleScenes = 0;
let canvas3DScene = null;
for (const scene of this.scenes) {
const {element} = scene;
if (element.modelIsVisible && scene.externalRenderer == null) {
++visibleScenes;
}
if (canvas3D.parentElement === scene.canvas.parentElement) {
canvas3DScene = scene;
}
}
const multipleScenesVisible = visibleScenes > 1;
if (canvas3DScene != null) {
const newlyMultiple =
multipleScenesVisible && !this.multipleScenesVisible;
const disappearing = !canvas3DScene.element.modelIsVisible;
if (newlyMultiple || disappearing) {
const {width, height} = this.sceneSize(canvas3DScene);
this.copyPixels(canvas3DScene, width, height);
canvas3D.parentElement!.removeChild(canvas3D);
}
}
this.multipleScenesVisible = multipleScenesVisible;
}
private rescaleCanvas(scene: ModelScene): boolean {
const {style} = scene.canvas;
if (!scene.shouldRender()) {
// The first frame we stop rendering the scene (because it stops moving),
// trigger one extra render at full scale.
if (scene.scaleStep != 0) {
scene.scaleStep = 0;
style.width = `${this.width}px`;
style.height = `${this.height}px`;
this.dispatchRenderScale(scene);
if (!this.multipleScenesVisible) {
this.canvas3D.style.width = `${this.width}px`;
this.canvas3D.style.height = `${this.height}px`;
}
} else {
return true; // Skip rendering
}
} else if (scene.scaleStep != this.scaleStep) {
// Update render scale
scene.scaleStep = this.scaleStep;
const scale = this.scaleFactor;
const width = Math.ceil(this.width / scale);
const height = Math.ceil(this.height / scale);
style.width = `${width}px`;
style.height = `${height}px`;
if (!this.multipleScenesVisible) {
this.canvas3D.style.width = `${width}px`;
this.canvas3D.style.height = `${height}px`;
}
this.dispatchRenderScale(scene);
}
return false; // Perform rendering
}
private sceneSize(scene: ModelScene) {

@@ -461,3 +452,3 @@ const {dpr} = this;

if (this.rescaleCanvas(scene)) {
if (!this.shouldRender(scene)) {
continue;

@@ -464,0 +455,0 @@ }

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc