import {
|
Cesium3DTileStyle,
|
FeatureDetection,
|
JulianDate,
|
defaultValue,
|
Matrix4,
|
Math as CesiumMath,
|
ResourceCache,
|
Resource,
|
ModelExperimental,
|
Cartesian3,
|
defined,
|
HeadingPitchRange,
|
when,
|
ShaderProgram,
|
ModelFeature,
|
Color,
|
StyleCommandsNeeded,
|
} from "../../../Source/Cesium.js";
|
import createScene from "../../createScene.js";
|
import loadAndZoomToModelExperimental from "./loadAndZoomToModelExperimental.js";
|
|
describe(
|
"Scene/ModelExperimental/ModelExperimental",
|
function () {
|
var webglStub = !!window.webglStub;
|
|
var boxTexturedGlbUrl =
|
"./Data/Models/GltfLoader/BoxTextured/glTF-Binary/BoxTextured.glb";
|
var buildingsMetadata =
|
"./Data/Models/GltfLoader/BuildingsMetadata/glTF/buildings-metadata.gltf";
|
var boxTexturedGltfUrl =
|
"./Data/Models/GltfLoader/BoxTextured/glTF/BoxTextured.gltf";
|
var microcosm = "./Data/Models/GltfLoader/Microcosm/glTF/microcosm.gltf";
|
var boxInstanced =
|
"./Data/Models/GltfLoader/BoxInstanced/glTF/box-instanced.gltf";
|
|
var scene;
|
|
beforeAll(function () {
|
scene = createScene();
|
});
|
|
afterAll(function () {
|
scene.destroyForSpecs();
|
});
|
|
afterEach(function () {
|
scene.primitives.removeAll();
|
ResourceCache.clearForSpecs();
|
});
|
|
function zoomTo(model, zoom) {
|
zoom = defaultValue(zoom, 4.0);
|
|
var camera = scene.camera;
|
var center = Matrix4.multiplyByPoint(
|
model.modelMatrix,
|
model.boundingSphere.center,
|
new Cartesian3()
|
);
|
var r = zoom * Math.max(model.boundingSphere.radius, camera.frustum.near);
|
camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, r));
|
}
|
|
function verifyRender(model, shouldRender) {
|
expect(model.ready).toBe(true);
|
zoomTo(model);
|
expect({
|
scene: scene,
|
time: JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")),
|
}).toRenderAndCall(function (rgba) {
|
if (shouldRender) {
|
expect(rgba).not.toEqual([0, 0, 0, 255]);
|
} else {
|
expect(rgba).toEqual([0, 0, 0, 255]);
|
}
|
});
|
}
|
|
it("initializes and renders from Uint8Array", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
|
var loadPromise = resource.fetchArrayBuffer();
|
return loadPromise.then(function (buffer) {
|
return loadAndZoomToModelExperimental(
|
{ gltf: new Uint8Array(buffer) },
|
scene
|
).then(function (model) {
|
expect(model.ready).toEqual(true);
|
expect(model._sceneGraph).toBeDefined();
|
expect(model._resourcesLoaded).toEqual(true);
|
verifyRender(model, true);
|
});
|
});
|
});
|
|
it("initializes feature table", function () {
|
return loadAndZoomToModelExperimental(
|
{ gltf: buildingsMetadata },
|
scene
|
).then(function (model) {
|
expect(model.ready).toEqual(true);
|
expect(model.featureTables).toBeDefined();
|
|
var featureTable = model.featureTables[0];
|
expect(featureTable).toBeDefined();
|
|
var featuresLength = featureTable.featuresLength;
|
expect(featuresLength).toEqual(10);
|
expect(featureTable.batchTexture).toBeDefined();
|
expect(featureTable.batchTexture._featuresLength).toEqual(10);
|
|
for (var i = 0; i < featuresLength; i++) {
|
var modelFeature = featureTable.getFeature(i);
|
expect(modelFeature instanceof ModelFeature).toEqual(true);
|
expect(modelFeature._featureId).toEqual(i);
|
expect(modelFeature.primitive).toEqual(model);
|
expect(modelFeature.featureTable).toEqual(featureTable);
|
}
|
|
expect(model._resourcesLoaded).toEqual(true);
|
});
|
});
|
|
it("initializes and renders from JSON object", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGltfUrl);
|
return resource.fetchJson().then(function (gltf) {
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: gltf,
|
basePath: boxTexturedGltfUrl,
|
},
|
scene
|
).then(function (model) {
|
expect(model.ready).toEqual(true);
|
expect(model._sceneGraph).toBeDefined();
|
expect(model._resourcesLoaded).toEqual(true);
|
verifyRender(model, true);
|
});
|
});
|
});
|
|
it("initializes and renders from JSON object with external buffers", function () {
|
var resource = Resource.createIfNeeded(microcosm);
|
return resource.fetchJson().then(function (gltf) {
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: gltf,
|
basePath: microcosm,
|
},
|
scene
|
).then(function (model) {
|
expect(model.ready).toEqual(true);
|
expect(model._sceneGraph).toBeDefined();
|
expect(model._resourcesLoaded).toEqual(true);
|
verifyRender(model, true);
|
});
|
});
|
});
|
|
it("rejects ready promise when texture fails to load", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGltfUrl);
|
return resource.fetchJson().then(function (gltf) {
|
gltf.images[0].uri = "non-existent-path.png";
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: gltf,
|
basePath: boxTexturedGltfUrl,
|
incrementallyLoadTextures: false,
|
},
|
scene
|
)
|
.then(function (model) {
|
fail();
|
})
|
.otherwise(function (error) {
|
expect(error).toBeDefined();
|
});
|
});
|
});
|
|
it("rejects ready promise when external buffer fails to load", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGltfUrl);
|
return resource.fetchJson().then(function (gltf) {
|
gltf.buffers[0].uri = "non-existent-path.bin";
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: gltf,
|
basePath: boxTexturedGltfUrl,
|
},
|
scene
|
)
|
.then(function (model) {
|
fail();
|
})
|
.otherwise(function (error) {
|
expect(error).toBeDefined();
|
});
|
});
|
});
|
|
it("show works", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
|
var loadPromise = resource.fetchArrayBuffer();
|
return loadPromise.then(function (buffer) {
|
return loadAndZoomToModelExperimental(
|
{ gltf: new Uint8Array(buffer), show: false },
|
scene
|
).then(function (model) {
|
expect(model.ready).toEqual(true);
|
expect(model._sceneGraph._drawCommands.length).toBeGreaterThan(0);
|
expect(model.show).toEqual(false);
|
verifyRender(model, false);
|
model.show = true;
|
expect(model.show).toEqual(true);
|
verifyRender(model, true);
|
});
|
});
|
});
|
|
it("debugShowBoundingVolume works", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
|
var loadPromise = resource.fetchArrayBuffer();
|
return loadPromise.then(function (buffer) {
|
return loadAndZoomToModelExperimental(
|
{ gltf: new Uint8Array(buffer), debugShowBoundingVolume: true },
|
scene
|
).then(function (model) {
|
var i;
|
scene.renderForSpecs();
|
var commandList = scene.frameState;
|
for (i = 0; i < commandList.length; i++) {
|
expect(commandList[i].debugShowBoundingVolume).toBe(true);
|
}
|
model.debugShowBoundingVolume = false;
|
expect(model._debugShowBoundingVolumeDirty).toBe(true);
|
scene.renderForSpecs();
|
for (i = 0; i < commandList.length; i++) {
|
expect(commandList[i].debugShowBoundingVolume).toBe(false);
|
}
|
});
|
});
|
});
|
|
it("boundingSphere works", function () {
|
var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
|
var loadPromise = resource.fetchArrayBuffer();
|
return loadPromise.then(function (buffer) {
|
return loadAndZoomToModelExperimental(
|
{ gltf: new Uint8Array(buffer), debugShowBoundingVolume: true },
|
scene
|
).then(function (model) {
|
var boundingSphere = model.boundingSphere;
|
expect(boundingSphere).toBeDefined();
|
expect(boundingSphere.center).toEqual(new Cartesian3());
|
expect(boundingSphere.radius).toEqualEpsilon(
|
0.8660254037844386,
|
CesiumMath.EPSILON8
|
);
|
});
|
});
|
});
|
|
it("renders model with style", function () {
|
return loadAndZoomToModelExperimental(
|
{ gltf: buildingsMetadata },
|
scene
|
).then(function (model) {
|
// Renders without style.
|
verifyRender(model, true);
|
|
// Renders with opaque style.
|
model.style = new Cesium3DTileStyle({
|
color: {
|
conditions: [["${height} > 1", "color('red')"]],
|
},
|
});
|
verifyRender(model, true);
|
|
// Renders with translucent style.
|
model.style = new Cesium3DTileStyle({
|
color: {
|
conditions: [["${height} > 1", "color('red', 0.5)"]],
|
},
|
});
|
verifyRender(model, true);
|
|
// Does not render when style disables show.
|
model.style = new Cesium3DTileStyle({
|
color: {
|
conditions: [["${height} > 1", "color('red', 0.0)"]],
|
},
|
});
|
verifyRender(model, false);
|
|
// Render when style is removed.
|
model.style = undefined;
|
verifyRender(model, true);
|
});
|
});
|
|
it("fromGltf throws with undefined options", function () {
|
expect(function () {
|
ModelExperimental.fromGltf();
|
}).toThrowDeveloperError();
|
});
|
|
it("fromGltf throws with undefined url", function () {
|
expect(function () {
|
ModelExperimental.fromGltf({});
|
}).toThrowDeveloperError();
|
});
|
|
it("picks box textured", function () {
|
if (FeatureDetection.isInternetExplorer()) {
|
// Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here.
|
return;
|
}
|
|
// This model gets clipped if log depth is disabled, so zoom out
|
// the camera just a little
|
var offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2);
|
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: boxTexturedGlbUrl,
|
offset: offset,
|
},
|
scene
|
).then(function (model) {
|
expect(scene).toPickAndCall(function (result) {
|
expect(result.primitive).toBeInstanceOf(ModelExperimental);
|
expect(result.primitive).toEqual(model);
|
});
|
});
|
});
|
|
it("doesn't pick when allowPicking is false", function () {
|
if (FeatureDetection.isInternetExplorer()) {
|
// Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here.
|
return;
|
}
|
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: boxTexturedGlbUrl,
|
allowPicking: false,
|
},
|
scene
|
).then(function () {
|
expect(scene).toPickAndCall(function (result) {
|
expect(result).toBeUndefined();
|
});
|
});
|
});
|
|
function setFeaturesWithOpacity(
|
featureTable,
|
opaqueFeaturesLength,
|
translucentFeaturesLength
|
) {
|
var i, feature;
|
for (i = 0; i < opaqueFeaturesLength; i++) {
|
feature = featureTable.getFeature(i);
|
feature.color = Color.RED;
|
}
|
for (
|
i = opaqueFeaturesLength;
|
i < opaqueFeaturesLength + translucentFeaturesLength;
|
i++
|
) {
|
feature = featureTable.getFeature(i);
|
feature.color = Color.RED.withAlpha(0.5);
|
}
|
}
|
|
it("resets draw commands when the style commands needed are changed", function () {
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: buildingsMetadata,
|
},
|
scene
|
).then(function (model) {
|
var featureTable = model.featureTables[model.featureTableId];
|
|
// Set all features to opaque.
|
setFeaturesWithOpacity(featureTable, 10, 0);
|
scene.renderForSpecs();
|
expect(featureTable.styleCommandsNeededDirty).toEqual(false);
|
expect(featureTable._styleCommandsNeeded).toEqual(
|
StyleCommandsNeeded.ALL_OPAQUE
|
);
|
|
// Set some features to translucent.
|
setFeaturesWithOpacity(featureTable, 8, 2);
|
scene.renderForSpecs();
|
expect(featureTable.styleCommandsNeededDirty).toEqual(true);
|
expect(featureTable._styleCommandsNeeded).toEqual(
|
StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT
|
);
|
|
// Set some more features to translucent.
|
setFeaturesWithOpacity(featureTable, 2, 8);
|
scene.renderForSpecs();
|
expect(featureTable.styleCommandsNeededDirty).toEqual(false);
|
expect(featureTable._styleCommandsNeeded).toEqual(
|
StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT
|
);
|
|
// Set all features to translucent.
|
setFeaturesWithOpacity(featureTable, 0, 10);
|
scene.renderForSpecs();
|
expect(featureTable.styleCommandsNeededDirty).toEqual(true);
|
expect(featureTable._styleCommandsNeeded).toEqual(
|
StyleCommandsNeeded.ALL_TRANSLUCENT
|
);
|
});
|
});
|
|
it("selects feature table for instanced feature ID attributes", function () {
|
if (webglStub) {
|
return;
|
}
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: boxInstanced,
|
featureIdAttributeIndex: 1,
|
},
|
scene
|
).then(function (model) {
|
expect(model.featureTableId).toEqual(1);
|
});
|
});
|
|
it("selects feature table for feature ID textures", function () {
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: microcosm,
|
},
|
scene
|
).then(function (model) {
|
expect(model.featureTableId).toEqual(0);
|
});
|
});
|
|
it("selects feature table for feature ID attributes", function () {
|
return loadAndZoomToModelExperimental(
|
{
|
gltf: buildingsMetadata,
|
},
|
scene
|
).then(function (model) {
|
expect(model.featureTableId).toEqual(0);
|
});
|
});
|
|
it("destroy works", function () {
|
spyOn(ShaderProgram.prototype, "destroy").and.callThrough();
|
return loadAndZoomToModelExperimental(
|
{ gltf: boxTexturedGlbUrl },
|
scene
|
).then(function (model) {
|
var resources = model._resources;
|
var loader = model._loader;
|
var resource;
|
|
var i;
|
for (i = 0; i < resources.length; i++) {
|
resource = resources[i];
|
if (defined(resource.isDestroyed)) {
|
expect(resource.isDestroyed()).toEqual(false);
|
}
|
}
|
expect(loader.isDestroyed()).toEqual(false);
|
expect(model.isDestroyed()).toEqual(false);
|
scene.primitives.remove(model);
|
if (!webglStub) {
|
expect(ShaderProgram.prototype.destroy).toHaveBeenCalled();
|
}
|
for (i = 0; i < resources.length - 1; i++) {
|
resource = resources[i];
|
if (defined(resource.isDestroyed)) {
|
expect(resource.isDestroyed()).toEqual(true);
|
}
|
}
|
expect(loader.isDestroyed()).toEqual(true);
|
expect(model.isDestroyed()).toEqual(true);
|
});
|
});
|
|
it("destroy doesn't destroy resources when they're in use", function () {
|
return when
|
.all([
|
loadAndZoomToModelExperimental({ gltf: boxTexturedGlbUrl }, scene),
|
loadAndZoomToModelExperimental({ gltf: boxTexturedGlbUrl }, scene),
|
])
|
.then(function (models) {
|
var cacheEntries = ResourceCache.cacheEntries;
|
var cacheKey;
|
var cacheEntry;
|
|
scene.primitives.remove(models[0]);
|
|
for (cacheKey in cacheEntries) {
|
if (cacheEntries.hasOwnProperty(cacheKey)) {
|
cacheEntry = cacheEntries[cacheKey];
|
expect(cacheEntry.referenceCount).toBeGreaterThan(0);
|
}
|
}
|
|
scene.primitives.remove(models[1]);
|
|
for (cacheKey in cacheEntries) {
|
if (cacheEntries.hasOwnProperty(cacheKey)) {
|
cacheEntry = cacheEntries[cacheKey];
|
expect(cacheEntry.referenceCount).toBe(0);
|
}
|
}
|
});
|
});
|
},
|
"WebGL"
|
);
|