UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

227 lines 9.97 kB
/* @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { BASE_OPACITY, EnvironmentMixin } from '../../features/environment.js'; import ModelViewerElementBase, { $scene } from '../../model-viewer-base.js'; import { Renderer } from '../../three-components/Renderer.js'; import { timePasses, waitForEvent } from '../../utilities.js'; import { assetPath, rafPasses } from '../helpers.js'; import { BasicSpecTemplate } from '../templates.js'; const expect = chai.expect; const ALT_BG_IMAGE_URL = assetPath('environments/white_furnace.hdr'); const HDR_BG_IMAGE_URL = assetPath('environments/spruit_sunrise_1k_HDR.hdr'); const MODEL_URL = assetPath('models/reflective-sphere.gltf'); const backgroundHasMap = (scene, url) => { return scene.background.userData.url === url; }; const modelUsingEnvMap = (scene, url) => { return scene.environment.userData.url === url; }; /** * Returns a promise that resolves when a given element is loaded * and has an environment map set that matches the passed in meta. */ const waitForLoadAndEnvMap = (element) => { const load = waitForEvent(element, 'load'); const envMap = waitForEvent(element[$scene], 'envmap-update'); return Promise.all([load, envMap]); }; suite('ModelViewerElementBase with EnvironmentMixin', () => { suiteTeardown(() => { Renderer.resetSingleton(); }); let nextId = 0; let tagName; let ModelViewerElement; let element; let scene; setup(() => { tagName = `model-viewer-environment-${nextId++}`; ModelViewerElement = class extends EnvironmentMixin(ModelViewerElementBase) { static get is() { return tagName; } }; customElements.define(tagName, ModelViewerElement); element = new ModelViewerElement(); scene = element[$scene]; }); teardown(() => element.parentNode && element.parentNode.removeChild(element)); BasicSpecTemplate(() => ModelViewerElement, () => tagName); test('only generates an environment when in the render tree', async () => { let environmentChangeCount = 0; const environmentChangeHandler = () => environmentChangeCount++; element.addEventListener('environment-change', environmentChangeHandler); element.style.display = 'none'; element.src = MODEL_URL; document.body.insertBefore(element, document.body.firstChild); await rafPasses(); expect(environmentChangeCount).to.be.equal(0); element.style.display = 'block'; await waitForEvent(element, 'environment-change'); expect(environmentChangeCount).to.be.equal(1); element.removeEventListener('environment-change', environmentChangeHandler); }); suite('with no skybox-image property', () => { let environmentChanges = 0; suite('and a src property', () => { setup(async () => { let onLoad = waitForLoadAndEnvMap(element); element.src = MODEL_URL; document.body.insertBefore(element, document.body.firstChild); environmentChanges = 0; scene.addEventListener('envmap-update', () => { environmentChanges++; }); await onLoad; }); teardown(() => { document.body.removeChild(element); }); test('applies a generated environment map on model', async function () { expect(modelUsingEnvMap(scene, null)).to.be.ok; }); test('changes the environment exactly once', async function () { expect(environmentChanges).to.be.eq(1); }); }); }); suite('exposure', () => { setup(async () => { element.src = MODEL_URL; document.body.insertBefore(element, document.body.firstChild); await waitForEvent(element, 'load'); scene.visible = true; }); teardown(() => { document.body.removeChild(element); }); test('changes the tone mapping exposure of the renderer', async () => { const renderer = Renderer.singleton; const originalToneMappingExposure = renderer.threeRenderer.toneMappingExposure; element.exposure = 2.0; await timePasses(); renderer.render(performance.now()); const newToneMappingExposure = renderer.threeRenderer.toneMappingExposure; expect(newToneMappingExposure) .to.be.greaterThan(originalToneMappingExposure); }); }); suite('shadow-intensity', () => { setup(async () => { element.src = MODEL_URL; document.body.insertBefore(element, document.body.firstChild); await waitForEvent(element, 'load'); }); teardown(() => { document.body.removeChild(element); }); test('changes the opacity of the static shadow', async () => { element.shadowIntensity = 1.0; await timePasses(); const newIntensity = scene.shadow.getIntensity(); expect(newIntensity).to.be.eq(BASE_OPACITY); }); }); suite('environment-image', () => { setup(async () => { let onLoad = waitForLoadAndEnvMap(element); element.setAttribute('src', MODEL_URL); element.setAttribute('environment-image', HDR_BG_IMAGE_URL); document.body.insertBefore(element, document.body.firstChild); await onLoad; }); teardown(() => { document.body.removeChild(element); }); test('applies environment-image environment map on model', () => { expect(modelUsingEnvMap(scene, element.environmentImage)).to.be.ok; }); suite('and environment-image subsequently removed', () => { setup(async () => { let envMapChanged = waitForEvent(scene, 'envmap-update'); element.removeAttribute('environment-image'); await envMapChanged; }); test('reapplies generated environment map on model', () => { expect(modelUsingEnvMap(scene, null)).to.be.ok; }); }); }); suite('with skybox-image property', () => { setup(async () => { let onLoad = waitForLoadAndEnvMap(element); element.setAttribute('src', MODEL_URL); element.setAttribute('skybox-image', HDR_BG_IMAGE_URL); document.body.insertBefore(element, document.body.firstChild); await onLoad; }); teardown(() => { document.body.removeChild(element); }); test('displays background with skybox-image', async function () { expect(backgroundHasMap(scene, element.skyboxImage)).to.be.ok; }); test('applies skybox-image environment map on model', async function () { expect(modelUsingEnvMap(scene, element.skyboxImage)).to.be.ok; }); suite('with an environment-image', () => { setup(async () => { const environmentChanged = waitForEvent(element, 'environment-change'); element.setAttribute('environment-image', ALT_BG_IMAGE_URL); await environmentChanged; }); test('prefers environment-image as environment map', () => { expect(modelUsingEnvMap(scene, ALT_BG_IMAGE_URL)).to.be.ok; }); suite('and environment-image subsequently removed', () => { setup(async () => { const environmentChanged = waitForEvent(element, 'environment-change'); element.removeAttribute('environment-image'); await environmentChanged; }); test('uses skybox-image as environment map', () => { expect(modelUsingEnvMap(scene, HDR_BG_IMAGE_URL)).to.be.ok; }); }); suite('and skybox-image subsequently removed', () => { setup(async () => { const environmentChanged = waitForEvent(element, 'environment-change'); element.removeAttribute('skybox-image'); await environmentChanged; }); test('continues using environment-image as environment map', () => { expect(modelUsingEnvMap(scene, ALT_BG_IMAGE_URL)).to.be.ok; }); test('removes the background', async function () { expect(scene.background).to.be.null; }); }); }); suite('and skybox-image subsequently removed', () => { setup(async () => { let envMapChanged = waitForEvent(scene, 'envmap-update'); element.removeAttribute('skybox-image'); await envMapChanged; }); test('removes the background', async function () { expect(scene.background).to.be.null; }); test('reapplies generated environment map on model', async function () { expect(modelUsingEnvMap(scene, null)).to.be.ok; }); }); }); }); //# sourceMappingURL=environment-spec.js.map