@google/model-viewer
Version:
Easily display interactive 3D models on the web and in AR!
275 lines (222 loc) • 10.3 kB
text/typescript
/* @license
* Copyright 2020 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 {expect} from 'chai';
import {Material, MeshStandardMaterial, Texture as ThreeTexture} from 'three';
import {$threeTexture} from '../../../features/scene-graph/image.js';
import {$lazyLoadGLTFInfo} from '../../../features/scene-graph/material.js';
import {Model} from '../../../features/scene-graph/model.js';
import {Texture} from '../../../features/scene-graph/texture.js';
import {$correlatedObjects} from '../../../features/scene-graph/three-dom-element.js';
import {ModelViewerElement} from '../../../model-viewer.js';
import {waitForEvent} from '../../../utilities.js';
import {assetPath} from '../../helpers.js';
const CUBES_GLTF_PATH = assetPath('models/cubes.gltf');
const HELMET_GLB_PATH = assetPath(
'models/glTF-Sample-Assets/Models/DamagedHelmet/glTF-Binary/DamagedHelmet.glb');
const ALPHA_BLEND_MODE_TEST = assetPath(
'models/glTF-Sample-Assets/Models/AlphaBlendModeTest/glTF-Binary/AlphaBlendModeTest.glb');
const REPLACEMENT_TEXTURE_PATH = assetPath(
'models/glTF-Sample-Assets/Models/BoxTextured/glTF/CesiumLogoFlat.png');
suite('scene-graph/material', () => {
suite('Test Texture Slots', () => {
let element: ModelViewerElement;
let texture: Texture|null;
let threeMaterials: Set<MeshStandardMaterial>;
setup(async () => {
element = new ModelViewerElement();
element.src = HELMET_GLB_PATH;
document.body.insertBefore(element, document.body.firstChild);
await waitForEvent(element, 'load');
texture = await element.createTexture(REPLACEMENT_TEXTURE_PATH);
threeMaterials = element.model!.materials[0][$correlatedObjects] as
Set<MeshStandardMaterial>;
});
teardown(() => {
document.body.removeChild(element);
texture = null;
});
test('Set a new base map', async () => {
element.model!.materials[0]
.pbrMetallicRoughness.baseColorTexture.setTexture(texture);
// Gets new UUID to compare with UUID of texture accessible through the
// material.
const newUUID: string|undefined = texture?.source[$threeTexture].uuid;
const threeTexture: ThreeTexture =
element.model!.materials[0]
.pbrMetallicRoughness.baseColorTexture?.texture
?.source[$threeTexture]!;
for (const material of threeMaterials as Set<MeshStandardMaterial>) {
expect(material.map).to.be.eq(threeTexture);
}
expect(threeTexture.uuid).to.be.equal(newUUID);
});
test('Set a new metallicRoughness map', async () => {
element.model!.materials[0]
.pbrMetallicRoughness.metallicRoughnessTexture.setTexture(texture);
// Gets new UUID to compare with UUID of texture accessible through the
// material.
const newUUID: string|undefined = texture?.source[$threeTexture]?.uuid;
const threeTexture: ThreeTexture =
element.model!.materials[0]
.pbrMetallicRoughness.metallicRoughnessTexture?.texture
?.source[$threeTexture]!;
for (const material of threeMaterials as Set<MeshStandardMaterial>) {
expect(material.metalnessMap).to.be.eq(threeTexture);
expect(material.roughnessMap).to.be.eq(threeTexture);
}
expect(threeTexture.uuid).to.be.equal(newUUID);
});
test('Set a new normal map', async () => {
element.model!.materials[0].normalTexture.setTexture(texture);
// Gets new UUID to compare with UUID of texture accessible through the
// material.
const newUUID: string|undefined = texture?.source[$threeTexture]?.uuid;
const threeTexture: ThreeTexture =
element.model!.materials[0]
.normalTexture?.texture?.source[$threeTexture]!;
for (const material of threeMaterials as Set<MeshStandardMaterial>) {
expect(material.normalMap).to.be.eq(threeTexture);
}
expect(threeTexture.uuid).to.be.equal(newUUID);
});
test('Set a new occlusion map', async () => {
element.model!.materials[0].occlusionTexture.setTexture(texture);
// Gets new UUID to compare with UUID of texture accessible through the
// material.
const newUUID: string|undefined = texture?.source[$threeTexture]?.uuid;
const threeTexture: ThreeTexture =
element.model!.materials[0]
.occlusionTexture?.texture?.source[$threeTexture]!;
for (const material of threeMaterials as Set<MeshStandardMaterial>) {
expect(material.aoMap).to.be.eq(threeTexture);
}
expect(threeTexture.uuid).to.be.equal(newUUID);
});
test('Set a new emissive map', async () => {
element.model!.materials[0].emissiveTexture.setTexture(texture);
// Gets new UUID to compare with UUID of texture accessible through the
// material.
const newUUID: string|undefined = texture?.source[$threeTexture]?.uuid;
const threeTexture: ThreeTexture =
element.model!.materials[0]
.emissiveTexture?.texture?.source[$threeTexture]!;
for (const material of threeMaterials as Set<MeshStandardMaterial>) {
expect(material.emissiveMap).to.be.eq(threeTexture);
}
expect(threeTexture.uuid).to.be.equal(newUUID);
});
});
suite('Material properties', () => {
let element: ModelViewerElement;
setup(async () => {
element = new ModelViewerElement();
});
teardown(() => {
document.body.removeChild(element);
});
const loadModel = async (path: string) => {
element.src = path;
document.body.insertBefore(element, document.body.firstChild);
await waitForEvent(element, 'load');
};
test('test alpha cutoff expect disabled by default', async () => {
await loadModel(HELMET_GLB_PATH);
expect((element.model!.materials[0]![$correlatedObjects]
?.values()
.next()
.value as Material)
.alphaTest)
.to.be.equal(0);
});
test('test alpha cutoff expect valid value as default', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[2].getAlphaCutoff()).to.be.equal(0.25);
});
test('test alpha cutoff test setting and getting', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
element.model!.materials[2].setAlphaCutoff(0.5);
expect(element.model!.materials[2].getAlphaCutoff()).to.be.equal(0.5);
});
test('test double sided expect default is false', async () => {
await loadModel(HELMET_GLB_PATH);
expect(element.model!.materials[0].getDoubleSided()).to.be.equal(false);
});
test('test double sided expect default is true', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[1].getDoubleSided()).to.be.equal(true);
});
test('test double sided setting and getting', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[1].getDoubleSided()).to.be.equal(true);
element.model!.materials[1].setDoubleSided(false);
expect(element.model!.materials[1].getDoubleSided()).to.be.equal(false);
element.model!.materials[1].setDoubleSided(true);
expect(element.model!.materials[1].getDoubleSided()).to.be.equal(true);
});
test('test alpha-mode, setting and getting', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
element.model!.materials[0].setAlphaMode('BLEND');
expect(element.model!.materials[0].getAlphaMode()).to.be.equal('BLEND');
element.model!.materials[0].setAlphaMode('MASK');
expect(element.model!.materials[0].getAlphaMode()).to.be.equal('MASK');
element.model!.materials[0].setAlphaMode('OPAQUE');
expect(element.model!.materials[0].getAlphaMode()).to.be.equal('OPAQUE');
});
test('test alpha-mode, expect default of opaque', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[0].getAlphaMode()).to.be.equal('OPAQUE');
});
test('test alpha-mode, expect default of blend', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[1].getAlphaMode()).to.be.equal('BLEND');
});
test('test alpha-mode, expect default of mask', async () => {
await loadModel(ALPHA_BLEND_MODE_TEST);
expect(element.model!.materials[2].getAlphaMode()).to.be.equal('MASK');
});
});
suite('Material lazy loading', () => {
let element: ModelViewerElement;
let model: Model;
setup(async () => {
element = new ModelViewerElement();
element.src = CUBES_GLTF_PATH;
document.body.insertBefore(element, document.body.firstChild);
await waitForEvent(element, 'load');
model = element.model as Model;
});
teardown(() => {
document.body.removeChild(element);
});
test('Accessing the name getter does not cause throw error.', async () => {
expect(model.materials[2].name).to.equal('red');
expect(model.materials[2][$lazyLoadGLTFInfo]).to.be.ok;
});
test(
'Accessing a getter of an unloaded material throws an error.',
async () => {
expect(() => {model.materials[2].pbrMetallicRoughness}).to.throw;
expect(model.materials[2].isLoaded).to.be.false;
});
test(
'Accessing a getter of a loaded material has valid data.', async () => {
await model.materials[2].ensureLoaded();
expect(model.materials[2].isLoaded).to.be.true;
expect(model.materials[2].name).to.equal('red');
const pbr = model.materials[2].pbrMetallicRoughness;
expect(pbr).to.be.ok;
});
});
});