From fdd8aa3912f61f4396c02599669403ba57e8b2c9 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 25 Jul 2022 16:58:57 -0400 Subject: [PATCH 01/10] Reorganize ModelExperimental tests --- .../ModelExperimental/ModelExperimental.js | 8 +- .../ModelExperimentalSpec.js | 3927 +++++++++-------- 2 files changed, 2015 insertions(+), 1920 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 0a5b66a06b42..998fe42a314d 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -66,7 +66,7 @@ import SplitDirection from "../SplitDirection.js"; * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. - * @param {Color} [options.color] A color that blends with the model's rendered color. + * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. @@ -195,8 +195,8 @@ export default function ModelExperimental(options) { this._id = options.id; this._idDirty = false; - const color = options.color; - this._color = defined(color) ? Color.clone(color) : undefined; + const color = defaultValue(options.color, Color.WHITE); + this._color = Color.clone(color); this._colorBlendMode = defaultValue( options.colorBlendMode, ColorBlendMode.HIGHLIGHT @@ -2359,7 +2359,7 @@ ModelExperimental.prototype.destroyModelResources = function () { * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. - * @param {Color} [options.color] A color that blends with the model's rendered color. + * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index 5d6ecffc899e..d044444b5f16 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -292,6 +292,18 @@ describe( return globe; } + 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("initializes and renders from Uint8Array", function () { const resource = Resource.createIfNeeded(boxTexturedGlbUrl); const loadPromise = resource.fetchArrayBuffer(); @@ -563,225 +575,6 @@ describe( }); }); - it("initializes with credit", function () { - const credit = new Credit("User Credit"); - const resource = Resource.createIfNeeded(boxTexturedGltfUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxTexturedGltfUrl, - credit: credit, - }, - scene - ).then(function (model) { - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.lightboxCredits.values; - const length = credits.length; - expect(length).toEqual(1); - expect(credits[0].credit.html).toEqual("User Credit"); - }); - }); - }); - - it("initializes with credit string", function () { - const creditString = "User Credit"; - const resource = Resource.createIfNeeded(boxTexturedGltfUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxTexturedGltfUrl, - credit: creditString, - }, - scene - ).then(function (model) { - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.lightboxCredits.values; - const length = credits.length; - expect(length).toEqual(1); - expect(credits[0].credit.html).toEqual(creditString); - }); - }); - }); - - it("gets copyrights from gltf", function () { - const resource = Resource.createIfNeeded(boxWithCreditsUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxWithCreditsUrl, - }, - scene - ).then(function (model) { - const expectedCredits = [ - "First Source", - "Second Source", - "Third Source", - ]; - - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.lightboxCredits.values; - const length = credits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(credits[i].credit.html).toEqual(expectedCredits[i]); - } - }); - }); - }); - - it("displays all types of credits", function () { - const resource = Resource.createIfNeeded(boxWithCreditsUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxWithCreditsUrl, - credit: "User Credit", - }, - scene - ).then(function (model) { - model._resourceCredits = [new Credit("Resource Credit")]; - const expectedCredits = [ - "User Credit", - "Resource Credit", - "First Source", - "Second Source", - "Third Source", - ]; - - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.lightboxCredits.values; - const length = credits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(credits[i].credit.html).toEqual(expectedCredits[i]); - } - }); - }); - }); - - it("initializes with showCreditsOnScreen", function () { - const resource = Resource.createIfNeeded(boxWithCreditsUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxWithCreditsUrl, - credit: "User Credit", - showCreditsOnScreen: true, - }, - scene - ).then(function (model) { - const expectedCredits = [ - "User Credit", - "First Source", - "Second Source", - "Third Source", - ]; - - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.screenCredits.values; - const length = credits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(credits[i].credit.html).toEqual(expectedCredits[i]); - } - }); - }); - }); - - it("changing showCreditsOnScreen works", function () { - const resource = Resource.createIfNeeded(boxWithCreditsUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxWithCreditsUrl, - credit: "User Credit", - showCreditsOnScreen: false, - }, - scene - ).then(function (model) { - const expectedCredits = [ - "User Credit", - "First Source", - "Second Source", - "Third Source", - ]; - - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const lightboxCredits = - creditDisplay._currentFrameCredits.lightboxCredits.values; - const screenCredits = - creditDisplay._currentFrameCredits.screenCredits.values; - - let length = lightboxCredits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(lightboxCredits[i].credit.html).toEqual(expectedCredits[i]); - } - expect(screenCredits.length).toEqual(0); - - model.showCreditsOnScreen = true; - scene.renderForSpecs(); - length = screenCredits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(screenCredits[i].credit.html).toEqual(expectedCredits[i]); - } - expect(lightboxCredits.length).toEqual(0); - - model.showCreditsOnScreen = false; - scene.renderForSpecs(); - length = lightboxCredits.length; - expect(length).toEqual(expectedCredits.length); - for (let i = 0; i < length; i++) { - expect(lightboxCredits[i].credit.html).toEqual(expectedCredits[i]); - } - expect(screenCredits.length).toEqual(0); - }); - }); - }); - - it("showCreditsOnScreen overrides existing credit setting", function () { - const resource = Resource.createIfNeeded(boxTexturedGltfUrl); - return resource.fetchJson().then(function (gltf) { - return loadAndZoomToModelExperimental( - { - gltf: gltf, - basePath: boxTexturedGltfUrl, - credit: new Credit("User Credit", false), - showCreditsOnScreen: true, - }, - scene - ).then(function (model) { - scene.renderForSpecs(); - const creditDisplay = scene.frameState.creditDisplay; - const credits = - creditDisplay._currentFrameCredits.screenCredits.values; - const length = credits.length; - expect(length).toEqual(1); - for (let i = 0; i < length; i++) { - expect(credits[i].credit.html).toEqual("User Credit"); - } - }); - }); - }); - it("show works", function () { const resource = Resource.createIfNeeded(boxTexturedGlbUrl); const loadPromise = resource.fetchArrayBuffer(); @@ -920,163 +713,6 @@ describe( }); }); - it("debugWireframe works for WebGL1 if enableDebugWireframe is true", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer), enableDebugWireframe: true }, - scene - ).then(function (model) { - verifyDebugWireframe(model, PrimitiveType.TRIANGLES); - }); - }); - }); - - it("debugWireframe does nothing in WebGL1 if enableDebugWireframe is false", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer), enableDebugWireframe: false }, - scene - ).then(function (model) { - const commandList = scene.frameState.commandList; - const commandCounts = []; - let i, command; - scene.renderForSpecs(); - for (i = 0; i < commandList.length; i++) { - command = commandList[i]; - expect(command.primitiveType).toBe(PrimitiveType.TRIANGLES); - commandCounts.push(command.count); - } - - model.debugWireframe = true; - expect(model._drawCommandsBuilt).toBe(false); - - scene.renderForSpecs(); - for (i = 0; i < commandList.length; i++) { - command = commandList[i]; - expect(command.primitiveType).toBe(PrimitiveType.TRIANGLES); - expect(command.count).toEqual(commandCounts[i]); - } - }); - }); - }); - - it("debugWireframe works for WebGL2", function () { - if (!sceneWithWebgl2.context.webgl2) { - return; - } - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer) }, - scene - ).then(function (model) { - verifyDebugWireframe(model, PrimitiveType.TRIANGLES, { - scene: sceneWithWebgl2, - }); - }); - }); - }); - - it("debugWireframe works for model without indices", function () { - return loadAndZoomToModelExperimental( - { gltf: triangleWithoutIndicesUrl, enableDebugWireframe: true }, - scene - ).then(function (model) { - verifyDebugWireframe(model, PrimitiveType.TRIANGLES, { - hasIndices: false, - }); - }); - }); - - it("debugWireframe works for model with triangle strip", function () { - return loadAndZoomToModelExperimental( - { gltf: triangleStripUrl, enableDebugWireframe: true }, - scene - ).then(function (model) { - verifyDebugWireframe(model, PrimitiveType.TRIANGLE_STRIP); - }); - }); - - it("debugWireframe works for model with triangle fan", function () { - return loadAndZoomToModelExperimental( - { gltf: triangleFanUrl, enableDebugWireframe: true }, - scene - ).then(function (model) { - verifyDebugWireframe(model, PrimitiveType.TRIANGLE_FAN); - }); - }); - - it("debugWireframe ignores points", function () { - return loadAndZoomToModelExperimental( - { gltf: pointCloudUrl, enableDebugWireframe: true }, - scene - ).then(function (model) { - let i; - scene.renderForSpecs(); - const commandList = scene.frameState.commandList; - for (i = 0; i < commandList.length; i++) { - const command = commandList[i]; - expect(command.primitiveType).toBe(PrimitiveType.POINTS); - expect(command.vertexArray.indexBuffer).toBeUndefined(); - } - - model.debugWireframe = true; - for (i = 0; i < commandList.length; i++) { - const command = commandList[i]; - expect(command.primitiveType).toBe(PrimitiveType.POINTS); - expect(command.vertexArray.indexBuffer).toBeUndefined(); - } - }); - }); - - it("debugShowBoundingVolume works", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer), debugShowBoundingVolume: true }, - scene - ).then(function (model) { - let i; - scene.renderForSpecs(); - const commandList = scene.frameState.commandList; - 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 () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer) }, - scene - ).then(function (model) { - const 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 () { let model; let style; @@ -1134,1349 +770,1746 @@ describe( }); }); - 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("initializes with id", function () { - // This model gets clipped if log depth is disabled, so zoom out - // the camera just a little - const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + describe("credits", function () { + it("initializes with credit", function () { + const credit = new Credit("User Credit"); + const resource = Resource.createIfNeeded(boxTexturedGltfUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxTexturedGltfUrl, + credit: credit, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.lightboxCredits.values; + const length = credits.length; + expect(length).toEqual(1); + expect(credits[0].credit.html).toEqual("User Credit"); + }); + }); + }); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - offset: offset, - id: boxTexturedGlbUrl, - }, - scene - ).then(function (model) { - expect(model.id).toBe(boxTexturedGlbUrl); - - const pickIds = model._pickIds; - expect(pickIds.length).toEqual(1); - expect(pickIds[0].object.id).toEqual(boxTexturedGlbUrl); + it("initializes with credit string", function () { + const creditString = "User Credit"; + const resource = Resource.createIfNeeded(boxTexturedGltfUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxTexturedGltfUrl, + credit: creditString, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.lightboxCredits.values; + const length = credits.length; + expect(length).toEqual(1); + expect(credits[0].credit.html).toEqual(creditString); + }); + }); }); - }); - - it("changing id works", function () { - // This model gets clipped if log depth is disabled, so zoom out - // the camera just a little - const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); - - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - offset: offset, - }, - scene - ).then(function (model) { - expect(model.id).toBeUndefined(); - - const pickIds = model._pickIds; - expect(pickIds.length).toEqual(1); - expect(pickIds[0].object.id).toBeUndefined(); - - model.id = boxTexturedGlbUrl; - scene.renderForSpecs(); - expect(pickIds[0].object.id).toBe(boxTexturedGlbUrl); - model.id = undefined; - scene.renderForSpecs(); - expect(pickIds[0].object.id).toBeUndefined(); + it("gets copyrights from gltf", function () { + const resource = Resource.createIfNeeded(boxWithCreditsUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxWithCreditsUrl, + }, + scene + ).then(function (model) { + const expectedCredits = [ + "First Source", + "Second Source", + "Third Source", + ]; + + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.lightboxCredits.values; + const length = credits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(credits[i].credit.html).toEqual(expectedCredits[i]); + } + }); + }); }); - }); - - 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 - const 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("displays all types of credits", function () { + const resource = Resource.createIfNeeded(boxWithCreditsUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxWithCreditsUrl, + credit: "User Credit", + }, + scene + ).then(function (model) { + model._resourceCredits = [new Credit("Resource Credit")]; + const expectedCredits = [ + "User Credit", + "Resource Credit", + "First Source", + "Second Source", + "Third Source", + ]; + + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.lightboxCredits.values; + const length = credits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(credits[i].credit.html).toEqual(expectedCredits[i]); + } + }); }); }); - }); - it("picks box textured with id", function () { - if (FeatureDetection.isInternetExplorer()) { - // Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here. - return; - } + it("initializes with showCreditsOnScreen", function () { + const resource = Resource.createIfNeeded(boxWithCreditsUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxWithCreditsUrl, + credit: "User Credit", + showCreditsOnScreen: true, + }, + scene + ).then(function (model) { + const expectedCredits = [ + "User Credit", + "First Source", + "Second Source", + "Third Source", + ]; + + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.screenCredits.values; + const length = credits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(credits[i].credit.html).toEqual(expectedCredits[i]); + } + }); + }); + }); - // This model gets clipped if log depth is disabled, so zoom out - // the camera just a little - const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + it("changing showCreditsOnScreen works", function () { + const resource = Resource.createIfNeeded(boxWithCreditsUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxWithCreditsUrl, + credit: "User Credit", + showCreditsOnScreen: false, + }, + scene + ).then(function (model) { + const expectedCredits = [ + "User Credit", + "First Source", + "Second Source", + "Third Source", + ]; + + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const lightboxCredits = + creditDisplay._currentFrameCredits.lightboxCredits.values; + const screenCredits = + creditDisplay._currentFrameCredits.screenCredits.values; + + let length = lightboxCredits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(lightboxCredits[i].credit.html).toEqual( + expectedCredits[i] + ); + } + expect(screenCredits.length).toEqual(0); + + model.showCreditsOnScreen = true; + scene.renderForSpecs(); + length = screenCredits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(screenCredits[i].credit.html).toEqual(expectedCredits[i]); + } + expect(lightboxCredits.length).toEqual(0); + + model.showCreditsOnScreen = false; + scene.renderForSpecs(); + length = lightboxCredits.length; + expect(length).toEqual(expectedCredits.length); + for (let i = 0; i < length; i++) { + expect(lightboxCredits[i].credit.html).toEqual( + expectedCredits[i] + ); + } + expect(screenCredits.length).toEqual(0); + }); + }); + }); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - offset: offset, - id: boxTexturedGlbUrl, - }, - scene - ).then(function (model) { - expect(scene).toPickAndCall(function (result) { - expect(result.primitive).toBeInstanceOf(ModelExperimental); - expect(result.primitive).toEqual(model); - expect(result.id).toEqual(boxTexturedGlbUrl); + it("showCreditsOnScreen overrides existing credit setting", function () { + const resource = Resource.createIfNeeded(boxTexturedGltfUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boxTexturedGltfUrl, + credit: new Credit("User Credit", false), + showCreditsOnScreen: true, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + const creditDisplay = scene.frameState.creditDisplay; + const credits = + creditDisplay._currentFrameCredits.screenCredits.values; + const length = credits.length; + expect(length).toEqual(1); + for (let i = 0; i < length; i++) { + expect(credits[i].credit.html).toEqual("User Credit"); + } + }); }); }); }); - 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; - } - - // This model gets clipped if log depth is disabled, so zoom out - // the camera just a little - const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + describe("debugWireframe", function () { + it("debugWireframe works for WebGL1 if enableDebugWireframe is true", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer), enableDebugWireframe: true }, + scene + ).then(function (model) { + verifyDebugWireframe(model, PrimitiveType.TRIANGLES); + }); + }); + }); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - allowPicking: false, - offset: offset, - }, - scene - ).then(function () { - expect(scene).toPickAndCall(function (result) { - expect(result).toBeUndefined(); + it("debugWireframe does nothing in WebGL1 if enableDebugWireframe is false", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer), enableDebugWireframe: false }, + scene + ).then(function (model) { + const commandList = scene.frameState.commandList; + const commandCounts = []; + let i, command; + scene.renderForSpecs(); + for (i = 0; i < commandList.length; i++) { + command = commandList[i]; + expect(command.primitiveType).toBe(PrimitiveType.TRIANGLES); + commandCounts.push(command.count); + } + + model.debugWireframe = true; + expect(model._drawCommandsBuilt).toBe(false); + + scene.renderForSpecs(); + for (i = 0; i < commandList.length; i++) { + command = commandList[i]; + expect(command.primitiveType).toBe(PrimitiveType.TRIANGLES); + expect(command.count).toEqual(commandCounts[i]); + } + }); }); }); - }); - function setFeaturesWithOpacity( - featureTable, - opaqueFeaturesLength, - translucentFeaturesLength - ) { - let 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("debugWireframe works for WebGL2", function () { + if (!sceneWithWebgl2.context.webgl2) { + return; + } + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer) }, + scene + ).then(function (model) { + verifyDebugWireframe(model, PrimitiveType.TRIANGLES, { + scene: sceneWithWebgl2, + }); + }); + }); + }); - it("resets draw commands when the style commands needed are changed", function () { - return loadAndZoomToModelExperimental( - { - gltf: buildingsMetadata, - }, - scene - ).then(function (model) { - const featureTable = model.featureTables[model.featureTableId]; + it("debugWireframe works for model without indices", function () { + return loadAndZoomToModelExperimental( + { gltf: triangleWithoutIndicesUrl, enableDebugWireframe: true }, + scene + ).then(function (model) { + verifyDebugWireframe(model, PrimitiveType.TRIANGLES, { + hasIndices: false, + }); + }); + }); - // Set all features to opaque. - setFeaturesWithOpacity(featureTable, 10, 0); - scene.renderForSpecs(); - expect(featureTable.styleCommandsNeededDirty).toEqual(false); - expect(featureTable._styleCommandsNeeded).toEqual( - StyleCommandsNeeded.ALL_OPAQUE - ); + it("debugWireframe works for model with triangle strip", function () { + return loadAndZoomToModelExperimental( + { gltf: triangleStripUrl, enableDebugWireframe: true }, + scene + ).then(function (model) { + verifyDebugWireframe(model, PrimitiveType.TRIANGLE_STRIP); + }); + }); - // Set some features to translucent. - setFeaturesWithOpacity(featureTable, 8, 2); - scene.renderForSpecs(); - expect(featureTable.styleCommandsNeededDirty).toEqual(true); - expect(featureTable._styleCommandsNeeded).toEqual( - StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT - ); + it("debugWireframe works for model with triangle fan", function () { + return loadAndZoomToModelExperimental( + { gltf: triangleFanUrl, enableDebugWireframe: true }, + scene + ).then(function (model) { + verifyDebugWireframe(model, PrimitiveType.TRIANGLE_FAN); + }); + }); - // 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 - ); + it("debugWireframe ignores points", function () { + return loadAndZoomToModelExperimental( + { gltf: pointCloudUrl, enableDebugWireframe: true }, + scene + ).then(function (model) { + let i; + scene.renderForSpecs(); + const commandList = scene.frameState.commandList; + for (i = 0; i < commandList.length; i++) { + const command = commandList[i]; + expect(command.primitiveType).toBe(PrimitiveType.POINTS); + expect(command.vertexArray.indexBuffer).toBeUndefined(); + } - // Set all features to translucent. - setFeaturesWithOpacity(featureTable, 0, 10); - scene.renderForSpecs(); - expect(featureTable.styleCommandsNeededDirty).toEqual(true); - expect(featureTable._styleCommandsNeeded).toEqual( - StyleCommandsNeeded.ALL_TRANSLUCENT - ); + model.debugWireframe = true; + for (i = 0; i < commandList.length; i++) { + const command = commandList[i]; + expect(command.primitiveType).toBe(PrimitiveType.POINTS); + expect(command.vertexArray.indexBuffer).toBeUndefined(); + } + }); }); }); - it("selects feature table for instanced feature ID attributes", function () { - if (webglStub) { - return; - } - return loadAndZoomToModelExperimental( - { - gltf: boxInstanced, - instanceFeatureIdLabel: "section", - }, - scene - ).then(function (model) { - expect(model.featureTableId).toEqual(1); + it("debugShowBoundingVolume works", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer), debugShowBoundingVolume: true }, + scene + ).then(function (model) { + let i; + scene.renderForSpecs(); + const commandList = scene.frameState.commandList; + 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("selects feature table for feature ID textures", function () { - return loadAndZoomToModelExperimental( - { - gltf: microcosm, - }, - scene - ).then(function (model) { - expect(model.featureTableId).toEqual(0); + it("boundingSphere works", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer) }, + scene + ).then(function (model) { + const boundingSphere = model.boundingSphere; + expect(boundingSphere).toBeDefined(); + expect(boundingSphere.center).toEqual(new Cartesian3()); + expect(boundingSphere.radius).toEqualEpsilon( + 0.8660254037844386, + CesiumMath.EPSILON8 + ); + }); }); }); - it("selects feature table for feature ID attributes", function () { - return loadAndZoomToModelExperimental( - { - gltf: buildingsMetadata, - }, - scene - ).then(function (model) { - expect(model.featureTableId).toEqual(0); - }); - }); + describe("picking and id", function () { + it("initializes with id", function () { + // This model gets clipped if log depth is disabled, so zoom out + // the camera just a little + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); - it("featureIdLabel setter works", function () { - return loadAndZoomToModelExperimental( - { - gltf: buildingsMetadata, - }, - scene - ).then(function (model) { - expect(model.featureIdLabel).toBe("featureId_0"); - model.featureIdLabel = "buildings"; - expect(model.featureIdLabel).toBe("buildings"); - model.featureIdLabel = 1; - expect(model.featureIdLabel).toBe("featureId_1"); - }); - }); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + offset: offset, + id: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + expect(model.id).toBe(boxTexturedGlbUrl); - it("instanceFeatureIdLabel setter works", function () { - if (webglStub) { - return; - } - return loadAndZoomToModelExperimental( - { - gltf: boxInstanced, - }, - scene - ).then(function (model) { - expect(model.instanceFeatureIdLabel).toBe("instanceFeatureId_0"); - model.instanceFeatureIdLabel = "section"; - expect(model.instanceFeatureIdLabel).toBe("section"); - model.instanceFeatureIdLabel = 1; - expect(model.instanceFeatureIdLabel).toBe("instanceFeatureId_1"); + const pickIds = model._pickIds; + expect(pickIds.length).toEqual(1); + expect(pickIds[0].object.id).toEqual(boxTexturedGlbUrl); + }); }); - }); - it("initializes with model matrix", function () { - const translation = new Cartesian3(10, 0, 0); - const transform = Matrix4.fromTranslation(translation); + it("changing id works", function () { + // This model gets clipped if log depth is disabled, so zoom out + // the camera just a little + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - modelMatrix: transform, - }, - scene - ).then(function (model) { - const sceneGraph = model.sceneGraph; - scene.renderForSpecs(); - expect(Matrix4.equals(sceneGraph.computedModelMatrix, transform)).toBe( - true - ); - expect(model.boundingSphere.center).toEqual(translation); - verifyRender(model, true); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + offset: offset, + }, + scene + ).then(function (model) { + expect(model.id).toBeUndefined(); + + const pickIds = model._pickIds; + expect(pickIds.length).toEqual(1); + expect(pickIds[0].object.id).toBeUndefined(); + + model.id = boxTexturedGlbUrl; + scene.renderForSpecs(); + expect(pickIds[0].object.id).toBe(boxTexturedGlbUrl); - expect(sceneGraph.computedModelMatrix).not.toBe(transform); - expect(model.modelMatrix).not.toBe(transform); + model.id = undefined; + scene.renderForSpecs(); + expect(pickIds[0].object.id).toBeUndefined(); + }); }); - }); - it("changing model matrix works", function () { - const translation = new Cartesian3(10, 0, 0); - const updateModelMatrix = spyOn( - ModelSceneGraph.prototype, - "updateModelMatrix" - ).and.callThrough(); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X }, - scene - ).then(function (model) { - verifyRender(model, true); - const sceneGraph = model.sceneGraph; + 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; + } - const transform = Matrix4.fromTranslation(translation); - Matrix4.multiplyTransformation( - model.modelMatrix, - transform, - model.modelMatrix - ); - scene.renderForSpecs(); + // This model gets clipped if log depth is disabled, so zoom out + // the camera just a little + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); - expect(updateModelMatrix).toHaveBeenCalled(); - expect(Matrix4.equals(sceneGraph.computedModelMatrix, transform)).toBe( - true - ); - // Keep the camera in-place to confirm that the model matrix moves the model out of view. - verifyRender(model, false, { - zoomToModel: false, + 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("changing model matrix affects bounding sphere", function () { - const translation = new Cartesian3(10, 0, 0); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X }, - scene - ).then(function (model) { - const transform = Matrix4.fromTranslation(translation); - expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); + it("picks box textured with id", function () { + if (FeatureDetection.isInternetExplorer()) { + // Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here. + return; + } - Matrix4.multiplyTransformation( - model.modelMatrix, - transform, - model.modelMatrix - ); - scene.renderForSpecs(); + // This model gets clipped if log depth is disabled, so zoom out + // the camera just a little + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); - expect(model.boundingSphere.center).toEqual(translation); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + offset: offset, + id: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + expect(scene).toPickAndCall(function (result) { + expect(result.primitive).toBeInstanceOf(ModelExperimental); + expect(result.primitive).toEqual(model); + expect(result.id).toEqual(boxTexturedGlbUrl); + }); + }); }); - }); - it("changing model matrix in 2D mode works if projectTo2D is false", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - modelMatrix: modelMatrix, - }, - scene2D - ).then(function (model) { - verifyRender(model, true, { - zoomToModel: false, - scene: scene2D, + it("picks box textured with a new id", 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 + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + offset: offset, + id: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + expect(scene).toPickAndCall(function (result) { + expect(result.primitive).toBeInstanceOf(ModelExperimental); + expect(result.primitive).toEqual(model); + expect(result.id).toEqual(boxTexturedGlbUrl); + }); + + model.id = "new id"; + expect(scene).toPickAndCall(function (result) { + expect(result.primitive).toBeInstanceOf(ModelExperimental); + expect(result.primitive).toEqual(model); + expect(result.id).toEqual("new id"); + }); }); + }); - model.modelMatrix = Matrix4.fromTranslation(new Cartesian3(10, 10, 10)); - verifyRender(model, false, { - zoomToModel: false, - scene: scene2D, + 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; + } + + // This model gets clipped if log depth is disabled, so zoom out + // the camera just a little + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + allowPicking: false, + offset: offset, + }, + scene + ).then(function () { + expect(scene).toPickAndCall(function (result) { + expect(result).toBeUndefined(); + }); }); }); - }); - it("changing model matrix in 2D mode throws if projectTo2D is true", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - modelMatrix: modelMatrix, - projectTo2D: true, - }, - scene2D - ).then(function (model) { - expect(function () { - model.modelMatrix = Matrix4.IDENTITY; - scene2D.renderForSpecs(); - }).toThrowDeveloperError(); + it("doesn't pick when model is hidden", 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 + const offset = new HeadingPitchRange(0, -CesiumMath.PI_OVER_FOUR, 2); + + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + offset: offset, + }, + scene + ).then(function (model) { + model.show = false; + expect(scene).toPickAndCall(function (result) { + expect(result).toBeUndefined(); + }); + }); }); }); - it("initializes with height reference", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); - expect(model._scene).toBe(sceneWithMockGlobe); - expect(model._clampedModelMatrix).toBeDefined(); + describe("features", function () { + function setFeaturesWithOpacity( + featureTable, + opaqueFeaturesLength, + translucentFeaturesLength + ) { + let 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) { + const 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("changing height reference works", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - heightReference: HeightReference.NONE, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(model.heightReference).toEqual(HeightReference.NONE); - expect(model._clampedModelMatrix).toBeUndefined(); + it("selects feature table for instanced feature ID attributes", function () { + if (webglStub) { + return; + } + return loadAndZoomToModelExperimental( + { + gltf: boxInstanced, + instanceFeatureIdLabel: "section", + }, + scene + ).then(function (model) { + expect(model.featureTableId).toEqual(1); + }); + }); - model.heightReference = HeightReference.CLAMP_TO_GROUND; - expect(model._heightDirty).toBe(true); + it("selects feature table for feature ID textures", function () { + return loadAndZoomToModelExperimental( + { + gltf: microcosm, + }, + scene + ).then(function (model) { + expect(model.featureTableId).toEqual(0); + }); + }); - sceneWithMockGlobe.renderForSpecs(); - expect(model._heightDirty).toBe(false); - expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); - expect(model._clampedModelMatrix).toBeDefined(); + it("selects feature table for feature ID attributes", function () { + return loadAndZoomToModelExperimental( + { + gltf: buildingsMetadata, + }, + scene + ).then(function (model) { + expect(model.featureTableId).toEqual(0); + }); }); - }); - it("creates height update callback when initializing with height reference", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + it("featureIdLabel setter works", function () { + return loadAndZoomToModelExperimental( + { + gltf: buildingsMetadata, + }, + scene + ).then(function (model) { + expect(model.featureIdLabel).toBe("featureId_0"); + model.featureIdLabel = "buildings"; + expect(model.featureIdLabel).toBe("buildings"); + model.featureIdLabel = 1; + expect(model.featureIdLabel).toBe("featureId_1"); + }); + }); + + it("instanceFeatureIdLabel setter works", function () { + if (webglStub) { + return; + } + return loadAndZoomToModelExperimental( + { + gltf: boxInstanced, + }, + scene + ).then(function (model) { + expect(model.instanceFeatureIdLabel).toBe("instanceFeatureId_0"); + model.instanceFeatureIdLabel = "section"; + expect(model.instanceFeatureIdLabel).toBe("section"); + model.instanceFeatureIdLabel = 1; + expect(model.instanceFeatureIdLabel).toBe("instanceFeatureId_1"); + }); }); }); - it("creates height update callback after setting height reference", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.NONE, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(model.heightReference).toEqual(HeightReference.NONE); - expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); + describe("model matrix", function () { + it("initializes with model matrix", function () { + const translation = new Cartesian3(10, 0, 0); + const transform = Matrix4.fromTranslation(translation); - model.heightReference = HeightReference.CLAMP_TO_GROUND; - expect(model.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); - sceneWithMockGlobe.renderForSpecs(); - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + upAxis: Axis.Z, + forwardAxis: Axis.X, + modelMatrix: transform, + }, + scene + ).then(function (model) { + const sceneGraph = model.sceneGraph; + scene.renderForSpecs(); + expect(sceneGraph.computedModelMatrix).toEqual(transform); + expect(model.boundingSphere.center).toEqual(translation); + verifyRender(model, true); + + expect(sceneGraph.computedModelMatrix).not.toBe(transform); + expect(model.modelMatrix).not.toBe(transform); + }); }); - }); - it("updates height reference callback when the height reference changes", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + it("changing model matrix works", function () { + const translation = new Cartesian3(10, 0, 0); + const updateModelMatrix = spyOn( + ModelSceneGraph.prototype, + "updateModelMatrix" + ).and.callThrough(); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X }, + scene + ).then(function (model) { + verifyRender(model, true); + const sceneGraph = model.sceneGraph; + + const transform = Matrix4.fromTranslation(translation); + Matrix4.multiplyTransformation( + model.modelMatrix, + transform, + model.modelMatrix + ); + scene.renderForSpecs(); - model.heightReference = HeightReference.RELATIVE_TO_GROUND; - sceneWithMockGlobe.renderForSpecs(); - expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + expect(updateModelMatrix).toHaveBeenCalled(); + expect(sceneGraph.computedModelMatrix).toEqual(transform); - sceneWithMockGlobe.globe.removedCallback = false; - model.heightReference = HeightReference.NONE; - sceneWithMockGlobe.renderForSpecs(); - expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); - expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); + // Keep the camera in-place to confirm that the model matrix moves the model out of view. + verifyRender(model, false, { + zoomToModel: false, + }); + }); }); - }); - it("updates height reference callback when the model matrix changes", function () { - const modelMatrix = Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Matrix4.clone(modelMatrix), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + it("changing model matrix affects bounding sphere", function () { + const translation = new Cartesian3(10, 0, 0); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X }, + scene + ).then(function (model) { + const transform = Matrix4.fromTranslation(translation); + expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); - // Modify the model matrix in place - const position = Cartesian3.fromDegrees(-73.0, 40.0); - model.modelMatrix[12] = position.x; - model.modelMatrix[13] = position.y; - model.modelMatrix[14] = position.z; + Matrix4.multiplyTransformation( + model.modelMatrix, + transform, + model.modelMatrix + ); + scene.renderForSpecs(); - sceneWithMockGlobe.renderForSpecs(); - expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + expect(model.boundingSphere.center).toEqual(translation); + }); + }); - // Replace the model matrix entirely - model.modelMatrix = modelMatrix; + it("changing model matrix in 2D mode works if projectTo2D is false", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + modelMatrix: modelMatrix, + }, + scene2D + ).then(function (model) { + verifyRender(model, true, { + zoomToModel: false, + scene: scene2D, + }); + + model.modelMatrix = Matrix4.fromTranslation( + new Cartesian3(10, 10, 10) + ); + verifyRender(model, false, { + zoomToModel: false, + scene: scene2D, + }); + }); + }); - sceneWithMockGlobe.renderForSpecs(); - expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + it("changing model matrix in 2D mode throws if projectTo2D is true", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + modelMatrix: modelMatrix, + projectTo2D: true, + }, + scene2D + ).then(function (model) { + expect(function () { + model.modelMatrix = Matrix4.IDENTITY; + scene2D.renderForSpecs(); + }).toThrowDeveloperError(); + }); }); }); - it("height reference callback updates the position", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + describe("height reference", function () { + it("initializes with height reference", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(model.heightReference).toEqual( + HeightReference.CLAMP_TO_GROUND + ); + expect(model._scene).toBe(sceneWithMockGlobe); + expect(model._clampedModelMatrix).toBeDefined(); + }); + }); - sceneWithMockGlobe.globe.callback( - Cartesian3.fromDegrees(-72.0, 40.0, 100.0) + it("changing height reference works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + heightReference: HeightReference.NONE, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(model.heightReference).toEqual(HeightReference.NONE); + expect(model._clampedModelMatrix).toBeUndefined(); + + model.heightReference = HeightReference.CLAMP_TO_GROUND; + expect(model._heightDirty).toBe(true); + + sceneWithMockGlobe.renderForSpecs(); + expect(model._heightDirty).toBe(false); + expect(model.heightReference).toEqual( + HeightReference.CLAMP_TO_GROUND + ); + expect(model._clampedModelMatrix).toBeDefined(); + }); + }); + + it("creates height update callback when initializing with height reference", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(model.heightReference).toEqual( + HeightReference.CLAMP_TO_GROUND + ); + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + }); + }); + + it("creates height update callback after setting height reference", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.NONE, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(model.heightReference).toEqual(HeightReference.NONE); + expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); + + model.heightReference = HeightReference.CLAMP_TO_GROUND; + expect(model.heightReference).toEqual( + HeightReference.CLAMP_TO_GROUND + ); + sceneWithMockGlobe.renderForSpecs(); + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + }); + }); + + it("updates height reference callback when the height reference changes", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + model.heightReference = HeightReference.RELATIVE_TO_GROUND; + sceneWithMockGlobe.renderForSpecs(); + expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + sceneWithMockGlobe.globe.removedCallback = false; + model.heightReference = HeightReference.NONE; + sceneWithMockGlobe.renderForSpecs(); + expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); + expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); + }); + }); + + it("updates height reference callback when the model matrix changes", function () { + const modelMatrix = Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) ); - const matrix = model._clampedModelMatrix; - const position = new Cartesian3(matrix[12], matrix[13], matrix[14]); - const ellipsoid = sceneWithMockGlobe.globe.ellipsoid; - const cartographic = ellipsoid.cartesianToCartographic(position); - expect(cartographic.height).toEqualEpsilon(100.0, CesiumMath.EPSILON9); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Matrix4.clone(modelMatrix), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + // Modify the model matrix in place + const position = Cartesian3.fromDegrees(-73.0, 40.0); + model.modelMatrix[12] = position.x; + model.modelMatrix[13] = position.y; + model.modelMatrix[14] = position.z; + + sceneWithMockGlobe.renderForSpecs(); + expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + // Replace the model matrix entirely + model.modelMatrix = modelMatrix; + + sceneWithMockGlobe.renderForSpecs(); + expect(sceneWithMockGlobe.globe.removedCallback).toEqual(true); + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + }); }); - }); - it("height reference accounts for change in terrain provider", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(model._heightDirty).toBe(false); - const terrainProvider = new CesiumTerrainProvider({ - url: "made/up/url", - requestVertexNormals: true, + it("height reference callback updates the position", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + sceneWithMockGlobe.globe.callback( + Cartesian3.fromDegrees(-72.0, 40.0, 100.0) + ); + const matrix = model._clampedModelMatrix; + const position = new Cartesian3(matrix[12], matrix[13], matrix[14]); + const ellipsoid = sceneWithMockGlobe.globe.ellipsoid; + const cartographic = ellipsoid.cartesianToCartographic(position); + expect(cartographic.height).toEqualEpsilon( + 100.0, + CesiumMath.EPSILON9 + ); }); - sceneWithMockGlobe.terrainProvider = terrainProvider; + }); + + it("height reference accounts for change in terrain provider", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(model._heightDirty).toBe(false); + const terrainProvider = new CesiumTerrainProvider({ + url: "made/up/url", + requestVertexNormals: true, + }); + sceneWithMockGlobe.terrainProvider = terrainProvider; - expect(model._heightDirty).toBe(true); - sceneWithMockGlobe.terrainProvider = undefined; + expect(model._heightDirty).toBe(true); + sceneWithMockGlobe.terrainProvider = undefined; + }); + }); + + it("throws when initializing height reference with no scene", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: undefined, + }, + sceneWithMockGlobe + ).catch(function (error) { + expect(error.message).toEqual( + "Height reference is not supported without a scene and globe." + ); + }); }); - }); - it("throws when initializing height reference with no scene", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: undefined, - }, - sceneWithMockGlobe - ).catch(function (error) { - expect(error.message).toEqual( - "Height reference is not supported without a scene and globe." - ); + it("throws when changing height reference with no scene", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.NONE, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(function () { + model.heightReference = HeightReference.CLAMP_TO_GROUND; + sceneWithMockGlobe.renderForSpecs(); + }).toThrowDeveloperError(); + }); }); - }); - it("throws when changing height reference with no scene", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.NONE, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(function () { - model.heightReference = HeightReference.CLAMP_TO_GROUND; - sceneWithMockGlobe.renderForSpecs(); - }).toThrowDeveloperError(); + it("throws when initializing height reference with no globe", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: scene, + }, + scene + ).catch(function (error) { + expect(error.message).toEqual( + "Height reference is not supported without a scene and globe." + ); + }); }); - }); - it("throws when initializing height reference with no globe", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: scene, - }, - scene - ).catch(function (error) { - expect(error.message).toEqual( - "Height reference is not supported without a scene and globe." - ); + it("throws when changing height reference with no globe", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + scene: scene, + }, + scene + ).then(function (model) { + expect(function () { + model.heightReference = HeightReference.CLAMP_TO_GROUND; + scene.renderForSpecs(); + }).toThrowDeveloperError(); + }); }); - }); - it("throws when changing height reference with no globe", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - scene: scene, - }, - scene - ).then(function (model) { - expect(function () { - model.heightReference = HeightReference.CLAMP_TO_GROUND; - scene.renderForSpecs(); - }).toThrowDeveloperError(); + it("destroys height reference callback", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(-72.0, 40.0) + ), + heightReference: HeightReference.CLAMP_TO_GROUND, + scene: sceneWithMockGlobe, + }, + sceneWithMockGlobe + ).then(function (model) { + expect(sceneWithMockGlobe.globe.callback).toBeDefined(); + + sceneWithMockGlobe.primitives.remove(model); + expect(model.isDestroyed()).toBe(true); + expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); + }); }); }); - it("initializes with distance display condition", function () { - const near = 10.0; - const far = 100.0; - const condition = new DistanceDisplayCondition(near, far); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - distanceDisplayCondition: condition, - }, - scene - ).then(function (model) { - verifyRender(model, false); + describe("distance display condition", function () { + it("initializes with distance display condition", function () { + const near = 10.0; + const far = 100.0; + const condition = new DistanceDisplayCondition(near, far); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + distanceDisplayCondition: condition, + }, + scene + ).then(function (model) { + verifyRender(model, false); + }); }); - }); - it("changing distance display condition works", function () { - const near = 10.0; - const far = 100.0; - const condition = new DistanceDisplayCondition(near, far); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); + it("changing distance display condition works", function () { + const near = 10.0; + const far = 100.0; + const condition = new DistanceDisplayCondition(near, far); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); - model.distanceDisplayCondition = condition; - verifyRender(model, false); + model.distanceDisplayCondition = condition; + verifyRender(model, false); - model.distanceDisplayCondition = undefined; - verifyRender(model, true); + model.distanceDisplayCondition = undefined; + verifyRender(model, true); + }); }); - }); - it("distanceDisplayCondition works with camera movement", function () { - const near = 10.0; - const far = 100.0; - const condition = new DistanceDisplayCondition(near, far); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); + it("distanceDisplayCondition works with camera movement", function () { + const near = 10.0; + const far = 100.0; + const condition = new DistanceDisplayCondition(near, far); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); - // Model distance is smaller than near value, should not render - model.distanceDisplayCondition = condition; - verifyRender(model, false); + // Model distance is smaller than near value, should not render + model.distanceDisplayCondition = condition; + verifyRender(model, false); - const frameState = scene.frameState; + const frameState = scene.frameState; - // Model distance is between near and far values, should render - frameState.camera.lookAt( - Cartesian3.ZERO, - new HeadingPitchRange(0.0, 0.0, (far + near) * 0.5) - ); - verifyRender(model, true, { - zoomToModel: false, - }); + // Model distance is between near and far values, should render + frameState.camera.lookAt( + Cartesian3.ZERO, + new HeadingPitchRange(0.0, 0.0, (far + near) * 0.5) + ); + verifyRender(model, true, { + zoomToModel: false, + }); - // Model distance is greater than far value, should not render - frameState.camera.lookAt( - Cartesian3.ZERO, - new HeadingPitchRange(0.0, 0.0, far + 10.0) - ); - verifyRender(model, false, { - zoomToModel: false, + // Model distance is greater than far value, should not render + frameState.camera.lookAt( + Cartesian3.ZERO, + new HeadingPitchRange(0.0, 0.0, far + 10.0) + ); + verifyRender(model, false, { + zoomToModel: false, + }); }); }); - }); - it("distanceDisplayCondition throws when near >= far", function () { - const near = 101.0; - const far = 100.0; - const condition = new DistanceDisplayCondition(near, far); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - }, - scene - ).then(function (model) { - expect(function () { - model.distanceDisplayCondition = condition; - }).toThrowDeveloperError(); + it("distanceDisplayCondition throws when near >= far", function () { + const near = 101.0; + const far = 100.0; + const condition = new DistanceDisplayCondition(near, far); + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + expect(function () { + model.distanceDisplayCondition = condition; + }).toThrowDeveloperError(); + }); }); }); - it("initializes with model color", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, color: Color.BLACK }, - scene - ).then(function (model) { - verifyRender(model, false); + describe("model color", function () { + it("initializes with model color", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, color: Color.BLACK }, + scene + ).then(function (model) { + verifyRender(model, false); + }); }); - }); - it("changing model color works", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl }, - scene - ).then(function (model) { - verifyRender(model, true); + it("changing model color works", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + verifyRender(model, true); - model.color = Color.BLACK; - verifyRender(model, false); + model.color = Color.BLACK; + verifyRender(model, false); - model.color = Color.RED; - verifyRender(model, true); + model.color = Color.RED; + verifyRender(model, true); - model.color = undefined; - verifyRender(model, true); + model.color = undefined; + verifyRender(model, true); + }); }); - }); - - it("invisible model doesn't render", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, color: Color.fromAlpha(Color.BLACK, 0.0) }, - scene - ).then(function (model) { - verifyRender(model, false); - // No commands should have been submitted - const commands = scene.frameState.commandList; - expect(commands.length).toBe(0); - }); - }); + it("invisible model doesn't render", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.fromAlpha(Color.BLACK, 0.0), + }, + scene + ).then(function (model) { + verifyRender(model, false); - it("initializes with silhouette size", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, silhouetteSize: 1.0 }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.OPAQUE); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.OPAQUE); + // No commands should have been submitted + const commands = scene.frameState.commandList; + expect(commands.length).toBe(0); + }); }); }); - it("changing silhouette size works", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(1); - expect(commands[0].renderState.stencilTest.enabled).toBe(false); - expect(commands[0].pass).toBe(Pass.OPAQUE); - - model.silhouetteSize = 1.0; - scene.renderForSpecs(); - expect(commands.length).toBe(2); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.OPAQUE); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.OPAQUE); - - model.silhouetteSize = 0.0; - scene.renderForSpecs(); - expect(commands.length).toBe(1); - expect(commands[0].renderState.stencilTest.enabled).toBe(false); - expect(commands[0].pass).toBe(Pass.OPAQUE); + describe("silhouette", function () { + it("initializes with silhouette size", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, silhouetteSize: 1.0 }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); + }); }); - }); - it("silhouette works with translucent color", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - silhouetteSize: 1.0, - silhouetteColor: Color.fromAlpha(Color.GREEN, 0.5), - }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.OPAQUE); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.TRANSLUCENT); - }); - }); + it("changing silhouette size works", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(1); + expect(commands[0].renderState.stencilTest.enabled).toBe(false); + expect(commands[0].pass).toBe(Pass.OPAQUE); - it("silhouette is disabled by invisible color", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, silhouetteSize: 1.0 }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.OPAQUE); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.OPAQUE); + model.silhouetteSize = 1.0; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); - model.silhouetteColor = Color.fromAlpha(Color.GREEN, 0.0); - scene.renderForSpecs(); - expect(commands.length).toBe(1); - expect(commands[0].renderState.stencilTest.enabled).toBe(false); - expect(commands[0].pass).toBe(Pass.OPAQUE); + model.silhouetteSize = 0.0; + scene.renderForSpecs(); + expect(commands.length).toBe(1); + expect(commands[0].renderState.stencilTest.enabled).toBe(false); + expect(commands[0].pass).toBe(Pass.OPAQUE); + }); }); - }); - it("silhouette works for invisible model", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - silhouetteSize: 1.0, - color: Color.fromAlpha(Color.WHITE, 0.0), - }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); - expect(commands[0].renderState.colorMask).toEqual({ - red: false, - green: false, - blue: false, - alpha: false, - }); - expect(commands[0].renderState.depthMask).toEqual(false); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.TRANSLUCENT); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + it("silhouette works with translucent color", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + silhouetteSize: 1.0, + silhouetteColor: Color.fromAlpha(Color.GREEN, 0.5), + }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + }); }); - }); - it("silhouette works for translucent model", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - silhouetteSize: 1.0, - color: Color.fromAlpha(Color.WHITE, 0.5), - }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); + it("silhouette is disabled by invisible color", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, silhouetteSize: 1.0 }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.OPAQUE); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.OPAQUE); - // Even though the silhouette color is opaque, the silhouette - // needs to be placed in the translucent pass. - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.TRANSLUCENT); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + model.silhouetteColor = Color.fromAlpha(Color.GREEN, 0.0); + scene.renderForSpecs(); + expect(commands.length).toBe(1); + expect(commands[0].renderState.stencilTest.enabled).toBe(false); + expect(commands[0].pass).toBe(Pass.OPAQUE); + }); }); - }); - it("silhouette works for translucent model and translucent silhouette color", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - silhouetteSize: 1.0, - color: Color.fromAlpha(Color.WHITE, 0.5), - silhouetteColor: Color.fromAlpha(Color.RED, 0.5), - }, - scene - ).then(function (model) { - const commands = scene.frameState.commandList; - scene.renderForSpecs(); - expect(commands.length).toBe(2); + it("silhouette works for invisible model", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + silhouetteSize: 1.0, + color: Color.fromAlpha(Color.WHITE, 0.0), + }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(2); + expect(commands[0].renderState.colorMask).toEqual({ + red: false, + green: false, + blue: false, + alpha: false, + }); + expect(commands[0].renderState.depthMask).toEqual(false); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + }); + }); + + it("silhouette works for translucent model", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + silhouetteSize: 1.0, + color: Color.fromAlpha(Color.WHITE, 0.5), + }, + scene + ).then(function (model) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + expect(commands.length).toBe(2); - expect(commands[0].renderState.stencilTest.enabled).toBe(true); - expect(commands[0].pass).toBe(Pass.TRANSLUCENT); - expect(commands[1].renderState.stencilTest.enabled).toBe(true); - expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + // Even though the silhouette color is opaque, the silhouette + // needs to be placed in the translucent pass. + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); + }); }); - }); - it("silhouette works for multiple models", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGltfUrl, - silhouetteSize: 1.0, - }, - scene - ).then(function (model) { + it("silhouette works for translucent model and translucent silhouette color", function () { return loadAndZoomToModelExperimental( { gltf: boxTexturedGltfUrl, silhouetteSize: 1.0, + color: Color.fromAlpha(Color.WHITE, 0.5), + silhouetteColor: Color.fromAlpha(Color.RED, 0.5), }, scene - ).then(function (model2) { + ).then(function (model) { const commands = scene.frameState.commandList; scene.renderForSpecs(); - const length = commands.length; - expect(length).toBe(4); - for (let i = 0; i < length; i++) { - const command = commands[i]; - expect(command.renderState.stencilTest.enabled).toBe(true); - expect(command.pass).toBe(Pass.OPAQUE); - } + expect(commands.length).toBe(2); - const reference1 = commands[0].renderState.stencilTest.reference; - const reference2 = commands[2].renderState.stencilTest.reference; - expect(reference2).toEqual(reference1 + 1); + expect(commands[0].renderState.stencilTest.enabled).toBe(true); + expect(commands[0].pass).toBe(Pass.TRANSLUCENT); + expect(commands[1].renderState.stencilTest.enabled).toBe(true); + expect(commands[1].pass).toBe(Pass.TRANSLUCENT); }); }); - }); - it("silhouette works with style", function () { - const style = new Cesium3DTileStyle({ - color: { - conditions: [["${height} > 1", "color('red', 0.5)"]], - }, + it("silhouette works for multiple models", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + silhouetteSize: 1.0, + }, + scene + ).then(function (model) { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + silhouetteSize: 1.0, + }, + scene + ).then(function (model2) { + const commands = scene.frameState.commandList; + scene.renderForSpecs(); + const length = commands.length; + expect(length).toBe(4); + for (let i = 0; i < length; i++) { + const command = commands[i]; + expect(command.renderState.stencilTest.enabled).toBe(true); + expect(command.pass).toBe(Pass.OPAQUE); + } + + const reference1 = commands[0].renderState.stencilTest.reference; + const reference2 = commands[2].renderState.stencilTest.reference; + expect(reference2).toEqual(reference1 + 1); + }); + }); }); - return loadAndZoomToModelExperimental( - { gltf: buildingsMetadata, silhouetteSize: 1.0 }, - scene - ).then(function (model) { - model.style = style; - scene.renderForSpecs(); - const commandList = scene.frameState.commandList; - expect(commandList.length).toBe(2); - expect(commandList[0].renderState.stencilTest.enabled).toBe(true); - expect(commandList[0].pass).toBe(Pass.TRANSLUCENT); - expect(commandList[1].renderState.stencilTest.enabled).toBe(true); - expect(commandList[1].pass).toBe(Pass.TRANSLUCENT); + + it("silhouette works with style", function () { + const style = new Cesium3DTileStyle({ + color: { + conditions: [["${height} > 1", "color('red', 0.5)"]], + }, + }); + return loadAndZoomToModelExperimental( + { gltf: buildingsMetadata, silhouetteSize: 1.0 }, + scene + ).then(function (model) { + model.style = style; + scene.renderForSpecs(); + const commandList = scene.frameState.commandList; + expect(commandList.length).toBe(2); + expect(commandList[0].renderState.stencilTest.enabled).toBe(true); + expect(commandList[0].pass).toBe(Pass.TRANSLUCENT); + expect(commandList[1].renderState.stencilTest.enabled).toBe(true); + expect(commandList[1].pass).toBe(Pass.TRANSLUCENT); + }); }); }); - it("initializes with light color", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, lightColor: Cartesian3.ZERO }, - scene - ).then(function (model) { - verifyRender(model, false); + describe("light color", function () { + it("initializes with light color", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, lightColor: Cartesian3.ZERO }, + scene + ).then(function (model) { + verifyRender(model, false); + }); }); - }); - it("changing light color works", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl }, - scene - ).then(function (model) { - model.lightColor = Cartesian3.ZERO; - verifyRender(model, false); + it("changing light color works", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + model.lightColor = Cartesian3.ZERO; + verifyRender(model, false); - model.lightColor = new Cartesian3(1.0, 0.0, 0.0); - verifyRender(model, true); + model.lightColor = new Cartesian3(1.0, 0.0, 0.0); + verifyRender(model, true); - model.lightColor = undefined; - verifyRender(model, true); + model.lightColor = undefined; + verifyRender(model, true); + }); }); - }); - it("light color doesn't affect unlit models", function () { - return loadAndZoomToModelExperimental({ gltf: boxUnlitUrl }, scene).then( - function (model) { + it("light color doesn't affect unlit models", function () { + return loadAndZoomToModelExperimental( + { gltf: boxUnlitUrl }, + scene + ).then(function (model) { verifyRender(model, true); model.lightColor = Cartesian3.ZERO; verifyRender(model, true); - } - ); - }); - - it("initializes with imageBasedLighting", function () { - const ibl = new ImageBasedLighting({ - imageBasedLightingFactor: Cartesian2.ZERO, - luminanceAtZenith: 0.5, - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl, imageBasedLighting: ibl }, - scene - ).then(function (model) { - expect(model.imageBasedLighting).toBe(ibl); - }); - }); - - it("creates default imageBasedLighting", function () { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl }, - scene - ).then(function (model) { - const imageBasedLighting = model.imageBasedLighting; - expect(imageBasedLighting).toBeDefined(); - expect( - Cartesian2.equals( - imageBasedLighting.imageBasedLightingFactor, - new Cartesian2(1, 1) - ) - ).toBe(true); - expect(imageBasedLighting.luminanceAtZenith).toBe(0.2); - expect( - imageBasedLighting.sphericalHarmonicCoefficients - ).toBeUndefined(); - expect(imageBasedLighting.specularEnvironmentMaps).toBeUndefined(); + }); }); }); - it("changing imageBasedLighting works", function () { - const imageBasedLighting = new ImageBasedLighting({ - imageBasedLightingFactor: Cartesian2.ZERO, - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGltfUrl }, - scene - ).then(function (model) { - const renderOptions = { - scene: scene, - time: new JulianDate(2456659.0004050927), - }; - - let result; - verifyRender(model, true); - expect(renderOptions).toRenderAndCall(function (rgba) { - result = rgba; + describe("imageBasedLighting", function () { + it("initializes with imageBasedLighting", function () { + const ibl = new ImageBasedLighting({ + imageBasedLightingFactor: Cartesian2.ZERO, + luminanceAtZenith: 0.5, }); - - model.imageBasedLighting = imageBasedLighting; - expect(renderOptions).toRenderAndCall(function (rgba) { - expect(rgba).not.toEqual(result); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, imageBasedLighting: ibl }, + scene + ).then(function (model) { + expect(model.imageBasedLighting).toBe(ibl); }); }); - }); - - it("initializes with scale", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - scale: 0.0, - }, - scene - ).then(function (model) { - scene.renderForSpecs(); - verifyRender(model, false); - expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(model.boundingSphere.radius).toEqual(0.0); - }); - }); + it("creates default imageBasedLighting", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + const imageBasedLighting = model.imageBasedLighting; + expect(imageBasedLighting).toBeDefined(); + expect( + Cartesian2.equals( + imageBasedLighting.imageBasedLightingFactor, + new Cartesian2(1, 1) + ) + ).toBe(true); + expect(imageBasedLighting.luminanceAtZenith).toBe(0.2); + expect( + imageBasedLighting.sphericalHarmonicCoefficients + ).toBeUndefined(); + expect(imageBasedLighting.specularEnvironmentMaps).toBeUndefined(); + }); + }); + + it("changing imageBasedLighting works", function () { + const imageBasedLighting = new ImageBasedLighting({ + imageBasedLightingFactor: Cartesian2.ZERO, + }); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; - it("changing scale works", function () { - const updateModelMatrix = spyOn( - ModelSceneGraph.prototype, - "updateModelMatrix" - ).and.callThrough(); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - }, - scene - ).then(function (model) { - verifyRender(model, true); - model.scale = 0.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, false); + let result; + verifyRender(model, true); + expect(renderOptions).toRenderAndCall(function (rgba) { + result = rgba; + }); - model.scale = 1.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, true); + model.imageBasedLighting = imageBasedLighting; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(result); + }); + }); }); }); - it("changing scale affects bounding sphere", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { + describe("scale", function () { + it("initializes with scale", function () { return loadAndZoomToModelExperimental( { - gltf: new Uint8Array(buffer), - scale: 10, + gltf: boxTexturedGlbUrl, + upAxis: Axis.Z, + forwardAxis: Axis.X, + scale: 0.0, }, scene ).then(function (model) { scene.renderForSpecs(); - const expectedRadius = 0.866; - const boundingSphere = model.boundingSphere; - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( - expectedRadius * 10.0, - CesiumMath.EPSILON3 - ); - - model.scale = 0.0; - scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqual(0.0); - - model.scale = 1.0; - scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + verifyRender(model, false); + expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(model.boundingSphere.radius).toEqual(0.0); }); }); - }); - it("initializes with minimumPixelSize", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { + it("changing scale works", function () { + const updateModelMatrix = spyOn( + ModelSceneGraph.prototype, + "updateModelMatrix" + ).and.callThrough(); return loadAndZoomToModelExperimental( { - gltf: new Uint8Array(buffer), + gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X, - minimumPixelSize: 1, - offset: new HeadingPitchRange(0, 0, 500), }, scene ).then(function (model) { - const renderOptions = { - zoomToModel: false, - }; - - const expectedRadius = 0.866; + verifyRender(model, true); + model.scale = 0.0; scene.renderForSpecs(); - verifyRender(model, true, renderOptions); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, false); - // Verify that minimumPixelSize didn't affect other parameters - expect(model.scale).toEqual(1.0); - expect(model.boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + model.scale = 1.0; + scene.renderForSpecs(); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, true); }); - }); - }); - - it("changing minimumPixelSize works", function () { - const updateModelMatrix = spyOn( - ModelSceneGraph.prototype, - "updateModelMatrix" - ).and.callThrough(); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - minimumPixelSize: 1, - offset: new HeadingPitchRange(0, 0, 500), - }, - scene - ).then(function (model) { - const renderOptions = { - zoomToModel: false, - }; - - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, true, renderOptions); - - model.minimumPixelSize = 0.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, false, renderOptions); - - model.minimumPixelSize = 1; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, true, renderOptions); - }); - }); - - it("changing minimumPixelSize doesn't affect bounding sphere or scale", function () { - const updateModelMatrix = spyOn( - ModelSceneGraph.prototype, - "updateModelMatrix" - ).and.callThrough(); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - minimumPixelSize: 1, - offset: new HeadingPitchRange(0, 0, 500), - }, - scene - ).then(function (model) { - const expectedRadius = 0.866; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - expect(model.scale).toEqual(1.0); - expect(model.boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); - - model.minimumPixelSize = 0.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - expect(model.scale).toEqual(1.0); - expect(model.boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + }); - model.minimumPixelSize = 1; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - expect(model.scale).toEqual(1.0); - expect(model.boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + it("changing scale affects bounding sphere", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { + gltf: new Uint8Array(buffer), + scale: 10, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + + const expectedRadius = 0.866; + const boundingSphere = model.boundingSphere; + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius * 10.0, + CesiumMath.EPSILON3 + ); + + model.scale = 0.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqual(0.0); + + model.scale = 1.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); + }); + }); }); }); - it("initializes with maximumScale", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { + describe("minimumPixelSize", function () { + it("initializes with minimumPixelSize", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { + gltf: new Uint8Array(buffer), + upAxis: Axis.Z, + forwardAxis: Axis.X, + minimumPixelSize: 1, + offset: new HeadingPitchRange(0, 0, 500), + }, + scene + ).then(function (model) { + const renderOptions = { + zoomToModel: false, + }; + + const expectedRadius = 0.866; + scene.renderForSpecs(); + verifyRender(model, true, renderOptions); + + // Verify that minimumPixelSize didn't affect other parameters + expect(model.scale).toEqual(1.0); + expect(model.boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); + }); + }); + }); + + it("changing minimumPixelSize works", function () { + const updateModelMatrix = spyOn( + ModelSceneGraph.prototype, + "updateModelMatrix" + ).and.callThrough(); return loadAndZoomToModelExperimental( { - gltf: new Uint8Array(buffer), + gltf: boxTexturedGlbUrl, upAxis: Axis.Z, forwardAxis: Axis.X, - maximumScale: 0.0, + minimumPixelSize: 1, + offset: new HeadingPitchRange(0, 0, 500), }, scene ).then(function (model) { - scene.renderForSpecs(); - verifyRender(model, false); - expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(model.boundingSphere.radius).toEqual(0.0); - }); - }); - }); + const renderOptions = { + zoomToModel: false, + }; - it("changing maximumScale works", function () { - const updateModelMatrix = spyOn( - ModelSceneGraph.prototype, - "updateModelMatrix" - ).and.callThrough(); - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - upAxis: Axis.Z, - forwardAxis: Axis.X, - scale: 2.0, - }, - scene - ).then(function (model) { - scene.renderForSpecs(); - verifyRender(model, true); + scene.renderForSpecs(); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, true, renderOptions); - model.maximumScale = 0.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, false); + model.minimumPixelSize = 0.0; + scene.renderForSpecs(); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, false, renderOptions); - model.maximumScale = 1.0; - scene.renderForSpecs(); - expect(updateModelMatrix).toHaveBeenCalled(); - verifyRender(model, true); + model.minimumPixelSize = 1; + scene.renderForSpecs(); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, true, renderOptions); + }); }); - }); - it("changing maximumScale affects bounding sphere", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { + it("changing minimumPixelSize doesn't affect bounding sphere or scale", function () { + const updateModelMatrix = spyOn( + ModelSceneGraph.prototype, + "updateModelMatrix" + ).and.callThrough(); return loadAndZoomToModelExperimental( { - gltf: new Uint8Array(buffer), - scale: 20, - maximumScale: 10, + gltf: boxTexturedGlbUrl, + upAxis: Axis.Z, + forwardAxis: Axis.X, + minimumPixelSize: 1, + offset: new HeadingPitchRange(0, 0, 500), }, scene ).then(function (model) { - scene.renderForSpecs(); - const expectedRadius = 0.866; - const boundingSphere = model.boundingSphere; - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( - expectedRadius * 10.0, + scene.renderForSpecs(); + expect(updateModelMatrix).toHaveBeenCalled(); + expect(model.scale).toEqual(1.0); + expect(model.boundingSphere.radius).toEqualEpsilon( + expectedRadius, CesiumMath.EPSILON3 ); - model.maximumScale = 0.0; + model.minimumPixelSize = 0.0; scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqual(0.0); + expect(updateModelMatrix).toHaveBeenCalled(); + expect(model.scale).toEqual(1.0); + expect(model.boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); - model.maximumScale = 1.0; + model.minimumPixelSize = 1; scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( + expect(updateModelMatrix).toHaveBeenCalled(); + expect(model.scale).toEqual(1.0); + expect(model.boundingSphere.radius).toEqualEpsilon( expectedRadius, CesiumMath.EPSILON3 ); @@ -2484,140 +2517,232 @@ describe( }); }); - it("changing maximumScale affects minimumPixelSize", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { + describe("maximumScale", function () { + it("initializes with maximumScale", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { + gltf: new Uint8Array(buffer), + upAxis: Axis.Z, + forwardAxis: Axis.X, + maximumScale: 0.0, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + verifyRender(model, false); + expect(model.boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(model.boundingSphere.radius).toEqual(0.0); + }); + }); + }); + + it("changing maximumScale works", function () { + const updateModelMatrix = spyOn( + ModelSceneGraph.prototype, + "updateModelMatrix" + ).and.callThrough(); return loadAndZoomToModelExperimental( { - gltf: new Uint8Array(buffer), - minimumPixelSize: 1, - maximumScale: 10, + gltf: boxTexturedGlbUrl, + upAxis: Axis.Z, + forwardAxis: Axis.X, + scale: 2.0, }, scene ).then(function (model) { scene.renderForSpecs(); - - const expectedRadius = 0.866; - const boundingSphere = model.boundingSphere; - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + verifyRender(model, true); model.maximumScale = 0.0; scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqual(0.0); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, false); - model.maximumScale = 10.0; + model.maximumScale = 1.0; scene.renderForSpecs(); - expect(boundingSphere.center).toEqual(Cartesian3.ZERO); - expect(boundingSphere.radius).toEqualEpsilon( - expectedRadius, - CesiumMath.EPSILON3 - ); + expect(updateModelMatrix).toHaveBeenCalled(); + verifyRender(model, true); }); }); - }); - it("enables back-face culling", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxBackFaceCullingUrl, - backFaceCulling: true, - offset: boxBackFaceCullingOffset, - }, - scene - ).then(function (model) { - verifyRender(model, false, { - zoomToModel: false, + it("changing maximumScale affects bounding sphere", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { + gltf: new Uint8Array(buffer), + scale: 20, + maximumScale: 10, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + + const expectedRadius = 0.866; + const boundingSphere = model.boundingSphere; + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius * 10.0, + CesiumMath.EPSILON3 + ); + + model.maximumScale = 0.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqual(0.0); + + model.maximumScale = 1.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); + }); }); }); - }); - it("disables back-face culling", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxBackFaceCullingUrl, - backFaceCulling: false, - offset: boxBackFaceCullingOffset, - }, - scene - ).then(function (model) { - verifyRender(model, true, { - zoomToModel: false, + it("changing maximumScale affects minimumPixelSize", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { + gltf: new Uint8Array(buffer), + minimumPixelSize: 1, + maximumScale: 10, + }, + scene + ).then(function (model) { + scene.renderForSpecs(); + + const expectedRadius = 0.866; + const boundingSphere = model.boundingSphere; + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); + + model.maximumScale = 0.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqual(0.0); + + model.maximumScale = 10.0; + scene.renderForSpecs(); + expect(boundingSphere.center).toEqual(Cartesian3.ZERO); + expect(boundingSphere.radius).toEqualEpsilon( + expectedRadius, + CesiumMath.EPSILON3 + ); + }); }); }); }); - it("ignores back-face culling when translucent", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxBackFaceCullingUrl, - backFaceCulling: true, - offset: boxBackFaceCullingOffset, - }, - scene - ).then(function (model) { - verifyRender(model, false, { - zoomToModel: false, + describe("back-face culling", function () { + it("enables back-face culling", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxBackFaceCullingUrl, + backFaceCulling: true, + offset: boxBackFaceCullingOffset, + }, + scene + ).then(function (model) { + verifyRender(model, false, { + zoomToModel: false, + }); }); + }); - model.color = new Color(0, 0, 1.0, 0.5); - - verifyRender(model, true, { - zoomToModel: false, + it("disables back-face culling", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxBackFaceCullingUrl, + backFaceCulling: false, + offset: boxBackFaceCullingOffset, + }, + scene + ).then(function (model) { + verifyRender(model, true, { + zoomToModel: false, + }); }); }); - }); - it("toggles back-face culling at runtime", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxBackFaceCullingUrl, - backFaceCulling: false, - offset: boxBackFaceCullingOffset, - }, - scene - ).then(function (model) { - verifyRender(model, true, { - zoomToModel: false, - }); + it("ignores back-face culling when translucent", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxBackFaceCullingUrl, + backFaceCulling: true, + offset: boxBackFaceCullingOffset, + }, + scene + ).then(function (model) { + verifyRender(model, false, { + zoomToModel: false, + }); - model.backFaceCulling = true; + model.color = new Color(0, 0, 1.0, 0.5); - verifyRender(model, false, { - zoomToModel: false, + verifyRender(model, true, { + zoomToModel: false, + }); }); }); - }); - it("ignores back-face culling toggles when translucent", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxBackFaceCullingUrl, - backFaceCulling: false, - offset: boxBackFaceCullingOffset, - color: new Color(0, 0, 1.0, 0.5), - }, - scene - ).then(function (model) { - verifyRender(model, true, { - zoomToModel: false, - }); + it("toggles back-face culling at runtime", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxBackFaceCullingUrl, + backFaceCulling: false, + offset: boxBackFaceCullingOffset, + }, + scene + ).then(function (model) { + verifyRender(model, true, { + zoomToModel: false, + }); - model.backFaceCulling = true; + model.backFaceCulling = true; - verifyRender(model, true, { - zoomToModel: false, + verifyRender(model, false, { + zoomToModel: false, + }); }); + }); + + it("ignores back-face culling toggles when translucent", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxBackFaceCullingUrl, + backFaceCulling: false, + offset: boxBackFaceCullingOffset, + color: new Color(0, 0, 1.0, 0.5), + }, + scene + ).then(function (model) { + verifyRender(model, true, { + zoomToModel: false, + }); - model.backFaceCulling = false; + model.backFaceCulling = true; - verifyRender(model, true, { - zoomToModel: false, + verifyRender(model, true, { + zoomToModel: false, + }); + + model.backFaceCulling = false; + + verifyRender(model, true, { + zoomToModel: false, + }); }); }); }); @@ -2656,329 +2781,359 @@ describe( }); }); - it("throws when given clipping planes attached to another model", function () { - const plane = new ClippingPlane(Cartesian3.UNIT_X, 0.0); - const clippingPlanes = new ClippingPlaneCollection({ - planes: [plane], - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl, clippingPlanes: clippingPlanes }, - scene - ) - .then(function (model) { - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl }, - scene - ); - }) - .then(function (model2) { - expect(function () { - model2.clippingPlanes = clippingPlanes; - }).toThrowDeveloperError(); + describe("clipping planes", function () { + it("throws when given clipping planes attached to another model", function () { + const plane = new ClippingPlane(Cartesian3.UNIT_X, 0.0); + const clippingPlanes = new ClippingPlaneCollection({ + planes: [plane], }); - }); - - it("updates clipping planes when clipping planes are enabled", function () { - const plane = new ClippingPlane(Cartesian3.UNIT_X, 0.0); - const clippingPlanes = new ClippingPlaneCollection({ - planes: [plane], + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl, clippingPlanes: clippingPlanes }, + scene + ) + .then(function (model) { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl }, + scene + ); + }) + .then(function (model2) { + expect(function () { + model2.clippingPlanes = clippingPlanes; + }).toThrowDeveloperError(); + }); }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl }, - scene - ).then(function (model) { - const gl = scene.frameState.context._gl; - spyOn(gl, "texImage2D").and.callThrough(); - scene.renderForSpecs(); - const callsBeforeClipping = gl.texImage2D.calls.count(); - - model.clippingPlanes = clippingPlanes; - scene.renderForSpecs(); - scene.renderForSpecs(); - // When clipping planes are created, we expect two calls to texImage2D - // (one for initial creation, and one for copying the data in) - // because clipping planes is stored inside a texture. - expect(gl.texImage2D.calls.count() - callsBeforeClipping).toEqual(2); - }); - }); + it("updates clipping planes when clipping planes are enabled", function () { + const plane = new ClippingPlane(Cartesian3.UNIT_X, 0.0); + const clippingPlanes = new ClippingPlaneCollection({ + planes: [plane], + }); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl }, + scene + ).then(function (model) { + const gl = scene.frameState.context._gl; + spyOn(gl, "texImage2D").and.callThrough(); - it("initializes and updates with clipping planes", function () { - const plane = new ClippingPlane(Cartesian3.UNIT_X, -2.5); - const clippingPlanes = new ClippingPlaneCollection({ - planes: [plane], - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl, clippingPlanes: clippingPlanes }, - scene - ).then(function (model) { - scene.renderForSpecs(); - verifyRender(model, false); + scene.renderForSpecs(); + const callsBeforeClipping = gl.texImage2D.calls.count(); - model.clippingPlanes = undefined; - scene.renderForSpecs(); - verifyRender(model, true); + model.clippingPlanes = clippingPlanes; + scene.renderForSpecs(); + scene.renderForSpecs(); + // When clipping planes are created, we expect two calls to texImage2D + // (one for initial creation, and one for copying the data in) + // because clipping planes is stored inside a texture. + expect(gl.texImage2D.calls.count() - callsBeforeClipping).toEqual(2); + }); }); - }); - it("updating clipping planes properties works", function () { - const direction = Cartesian3.multiplyByScalar( - Cartesian3.UNIT_X, - -1, - new Cartesian3() - ); - const plane = new ClippingPlane(direction, 0.0); - const clippingPlanes = new ClippingPlaneCollection({ - planes: [plane], - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl }, - scene - ).then(function (model) { - let modelColor; - scene.renderForSpecs(); - verifyRender(model, true); - expect(scene).toRenderAndCall(function (rgba) { - modelColor = rgba; + it("initializes and updates with clipping planes", function () { + const plane = new ClippingPlane(Cartesian3.UNIT_X, -2.5); + const clippingPlanes = new ClippingPlaneCollection({ + planes: [plane], }); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl, clippingPlanes: clippingPlanes }, + scene + ).then(function (model) { + scene.renderForSpecs(); + verifyRender(model, false); - // The clipping plane should cut the model in half such that - // we see the back faces. - model.clippingPlanes = clippingPlanes; - scene.renderForSpecs(); - expect(scene).toRenderAndCall(function (rgba) { - expect(rgba).not.toEqual(modelColor); + model.clippingPlanes = undefined; + scene.renderForSpecs(); + verifyRender(model, true); }); + }); - plane.distance = 10.0; // Move the plane away from the model - scene.renderForSpecs(); - expect(scene).toRenderAndCall(function (rgba) { - expect(rgba).toEqual(modelColor); + it("updating clipping planes properties works", function () { + const direction = Cartesian3.multiplyByScalar( + Cartesian3.UNIT_X, + -1, + new Cartesian3() + ); + const plane = new ClippingPlane(direction, 0.0); + const clippingPlanes = new ClippingPlaneCollection({ + planes: [plane], }); - }); - }); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl }, + scene + ).then(function (model) { + let modelColor; + scene.renderForSpecs(); + verifyRender(model, true); + expect(scene).toRenderAndCall(function (rgba) { + modelColor = rgba; + }); - it("clipping planes apply edge styling", function () { - const plane = new ClippingPlane(Cartesian3.UNIT_X, 0); - const clippingPlanes = new ClippingPlaneCollection({ - planes: [plane], - edgeWidth: 25.0, // make large enough to show up on the render - edgeColor: Color.BLUE, + // The clipping plane should cut the model in half such that + // we see the back faces. + model.clippingPlanes = clippingPlanes; + scene.renderForSpecs(); + expect(scene).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(modelColor); + }); + + plane.distance = 10.0; // Move the plane away from the model + scene.renderForSpecs(); + expect(scene).toRenderAndCall(function (rgba) { + expect(rgba).toEqual(modelColor); + }); + }); }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl }, - scene - ).then(function (model) { - let modelColor; - scene.renderForSpecs(); - verifyRender(model, true); - expect(scene).toRenderAndCall(function (rgba) { - modelColor = rgba; + it("clipping planes apply edge styling", function () { + const plane = new ClippingPlane(Cartesian3.UNIT_X, 0); + const clippingPlanes = new ClippingPlaneCollection({ + planes: [plane], + edgeWidth: 25.0, // make large enough to show up on the render + edgeColor: Color.BLUE, }); - model.clippingPlanes = clippingPlanes; + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl }, + scene + ).then(function (model) { + let modelColor; + scene.renderForSpecs(); + verifyRender(model, true); + expect(scene).toRenderAndCall(function (rgba) { + modelColor = rgba; + }); - scene.renderForSpecs(); - expect(scene).toRenderAndCall(function (rgba) { - expect(rgba).toEqual([0, 0, 255, 255]); - }); + model.clippingPlanes = clippingPlanes; - clippingPlanes.edgeWidth = 0.0; - scene.renderForSpecs(); - expect(scene).toRenderAndCall(function (rgba) { - expect(rgba).toEqual(modelColor); + scene.renderForSpecs(); + expect(scene).toRenderAndCall(function (rgba) { + expect(rgba).toEqual([0, 0, 255, 255]); + }); + + clippingPlanes.edgeWidth = 0.0; + scene.renderForSpecs(); + expect(scene).toRenderAndCall(function (rgba) { + expect(rgba).toEqual(modelColor); + }); }); }); - }); - it("clipping planes union regions", function () { - const clippingPlanes = new ClippingPlaneCollection({ - planes: [ - new ClippingPlane(Cartesian3.UNIT_Z, 5.0), - new ClippingPlane(Cartesian3.UNIT_X, -2.0), - ], - unionClippingRegions: true, - }); - return loadAndZoomToModelExperimental( - { gltf: boxTexturedGlbUrl }, - scene - ).then(function (model) { - scene.renderForSpecs(); - verifyRender(model, true); + it("clipping planes union regions", function () { + const clippingPlanes = new ClippingPlaneCollection({ + planes: [ + new ClippingPlane(Cartesian3.UNIT_Z, 5.0), + new ClippingPlane(Cartesian3.UNIT_X, -2.0), + ], + unionClippingRegions: true, + }); + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGlbUrl }, + scene + ).then(function (model) { + scene.renderForSpecs(); + verifyRender(model, true); - // These planes are defined such that the model is outside their union. - model.clippingPlanes = clippingPlanes; - scene.renderForSpecs(); - verifyRender(model, false); + // These planes are defined such that the model is outside their union. + model.clippingPlanes = clippingPlanes; + scene.renderForSpecs(); + verifyRender(model, false); - model.clippingPlanes.unionClippingRegions = false; - scene.renderForSpecs(); - verifyRender(model, true); + model.clippingPlanes.unionClippingRegions = false; + scene.renderForSpecs(); + verifyRender(model, true); + }); }); - }); - it("setArticulationStage throws when model is not ready", function () { - const model = ModelExperimental.fromGltf({ - url: boxArticulationsUrl, + it("destroys attached ClippingPlaneCollections", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + const clippingPlanes = new ClippingPlaneCollection({ + planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], + }); + + model.clippingPlanes = clippingPlanes; + expect(model.isDestroyed()).toEqual(false); + expect(clippingPlanes.isDestroyed()).toEqual(false); + + scene.primitives.remove(model); + expect(model.isDestroyed()).toEqual(true); + expect(clippingPlanes.isDestroyed()).toEqual(true); + }); }); - expect(function () { - model.setArticulationStage("SampleArticulation MoveX", 10.0); - }).toThrowDeveloperError(); + it("destroys ClippingPlaneCollections that are detached", function () { + let clippingPlanes; + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + clippingPlanes = new ClippingPlaneCollection({ + planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], + }); + model.clippingPlanes = clippingPlanes; + expect(clippingPlanes.isDestroyed()).toBe(false); + + model.clippingPlanes = undefined; + expect(clippingPlanes.isDestroyed()).toBe(true); + }); + }); }); - it("setArticulationStage throws with invalid value", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { + describe("AGI_articulations", function () { + it("setArticulationStage throws when model is not ready", function () { + const model = ModelExperimental.fromGltf({ + url: boxArticulationsUrl, + }); + expect(function () { - model.setArticulationStage("SampleArticulation MoveX", "bad"); + model.setArticulationStage("SampleArticulation MoveX", 10.0); }).toThrowDeveloperError(); }); - }); - it("applyArticulations throws when model is not ready", function () { - const model = ModelExperimental.fromGltf({ - url: boxArticulationsUrl, + it("setArticulationStage throws with invalid value", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + expect(function () { + model.setArticulationStage("SampleArticulation MoveX", "bad"); + }).toThrowDeveloperError(); + }); }); - expect(function () { - model.applyArticulations(); - }).toThrowDeveloperError(); - }); + it("applyArticulations throws when model is not ready", function () { + const model = ModelExperimental.fromGltf({ + url: boxArticulationsUrl, + }); - it("applies articulations", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); + expect(function () { + model.applyArticulations(); + }).toThrowDeveloperError(); + }); + + it("applies articulations", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); - model.setArticulationStage("SampleArticulation MoveX", 10.0); - model.applyArticulations(); - verifyRender(model, false); + model.setArticulationStage("SampleArticulation MoveX", 10.0); + model.applyArticulations(); + verifyRender(model, false); - model.setArticulationStage("SampleArticulation MoveX", 0.0); - model.applyArticulations(); - verifyRender(model, true); + model.setArticulationStage("SampleArticulation MoveX", 0.0); + model.applyArticulations(); + verifyRender(model, true); - model.setArticulationStage("SampleArticulation Size", 0.0); - model.applyArticulations(); - verifyRender(model, false); + model.setArticulationStage("SampleArticulation Size", 0.0); + model.applyArticulations(); + verifyRender(model, false); - model.setArticulationStage("SampleArticulation Size", 1.0); - model.applyArticulations(); - verifyRender(model, true); + model.setArticulationStage("SampleArticulation Size", 1.0); + model.applyArticulations(); + verifyRender(model, true); + }); }); }); - it("getNode throws when model is not ready", function () { - const model = ModelExperimental.fromGltf({ - url: boxArticulationsUrl, - }); - - expect(function () { - model.getNode("Root"); - }).toThrowDeveloperError(); - }); + describe("getNode", function () { + it("getNode throws when model is not ready", function () { + const model = ModelExperimental.fromGltf({ + url: boxArticulationsUrl, + }); - it("getNode throws when name is undefined", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { expect(function () { - model.getNode(); + model.getNode("Root"); }).toThrowDeveloperError(); }); - }); - it("getNode returns undefined for nonexistent node", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - const node = model.getNode("I don't exist"); - expect(node).toBeUndefined(); + it("getNode throws when name is undefined", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + expect(function () { + model.getNode(); + }).toThrowDeveloperError(); + }); }); - }); - - it("getNode returns a node", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - const node = model.getNode("Root"); - expect(node).toBeDefined(); - expect(node.name).toEqual("Root"); - expect(node.id).toEqual(0); - expect(node.show).toEqual(true); - expect(node.matrix).toEqual(boxArticulationsMatrix); - expect(node.originalMatrix).toEqual(boxArticulationsMatrix); + it("getNode returns undefined for nonexistent node", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + const node = model.getNode("I don't exist"); + expect(node).toBeUndefined(); + }); }); - }); - it("changing node.show works", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); - const node = model.getNode("Root"); - expect(node.show).toEqual(true); + it("getNode returns a node", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + const node = model.getNode("Root"); - node.show = false; - verifyRender(model, false); + expect(node).toBeDefined(); + expect(node.name).toEqual("Root"); + expect(node.id).toEqual(0); + expect(node.show).toEqual(true); + expect(node.matrix).toEqual(boxArticulationsMatrix); + expect(node.originalMatrix).toEqual(boxArticulationsMatrix); + }); }); - }); - it("changing node.show works", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); - const node = model.getNode("Root"); - expect(node.show).toEqual(true); + it("changing node.show works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); + const node = model.getNode("Root"); + expect(node.show).toEqual(true); - node.show = false; - verifyRender(model, false); + node.show = false; + verifyRender(model, false); + }); }); - }); - it("changing node.matrix works", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxArticulationsUrl, - }, - scene - ).then(function (model) { - verifyRender(model, true); - const node = model.getNode("Root"); - expect(node.matrix).toEqual(boxArticulationsMatrix); - expect(node.originalMatrix).toEqual(boxArticulationsMatrix); + it("changing node.matrix works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxArticulationsUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); + const node = model.getNode("Root"); + expect(node.matrix).toEqual(boxArticulationsMatrix); + expect(node.originalMatrix).toEqual(boxArticulationsMatrix); - node.matrix = Matrix4.fromTranslation(new Cartesian3(10, 0, 0)); - // The model's bounding sphere doesn't account for animations, - // so the camera will not account for the node's new transform. - verifyRender(model, false); + node.matrix = Matrix4.fromTranslation(new Cartesian3(10, 0, 0)); + // The model's bounding sphere doesn't account for animations, + // so the camera will not account for the node's new transform. + verifyRender(model, false); + }); }); }); @@ -3016,66 +3171,6 @@ describe( }); }); - it("destroys attached ClippingPlaneCollections", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - }, - scene - ).then(function (model) { - const clippingPlanes = new ClippingPlaneCollection({ - planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], - }); - - model.clippingPlanes = clippingPlanes; - expect(model.isDestroyed()).toEqual(false); - expect(clippingPlanes.isDestroyed()).toEqual(false); - - scene.primitives.remove(model); - expect(model.isDestroyed()).toEqual(true); - expect(clippingPlanes.isDestroyed()).toEqual(true); - }); - }); - - it("destroys ClippingPlaneCollections that are detached", function () { - let clippingPlanes; - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - }, - scene - ).then(function (model) { - clippingPlanes = new ClippingPlaneCollection({ - planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], - }); - model.clippingPlanes = clippingPlanes; - expect(clippingPlanes.isDestroyed()).toBe(false); - - model.clippingPlanes = undefined; - expect(clippingPlanes.isDestroyed()).toBe(true); - }); - }); - - it("destroys height reference callback", function () { - return loadAndZoomToModelExperimental( - { - gltf: boxTexturedGlbUrl, - modelMatrix: Transforms.eastNorthUpToFixedFrame( - Cartesian3.fromDegrees(-72.0, 40.0) - ), - heightReference: HeightReference.CLAMP_TO_GROUND, - scene: sceneWithMockGlobe, - }, - sceneWithMockGlobe - ).then(function (model) { - expect(sceneWithMockGlobe.globe.callback).toBeDefined(); - - sceneWithMockGlobe.primitives.remove(model); - expect(model.isDestroyed()).toBe(true); - expect(sceneWithMockGlobe.globe.callback).toBeUndefined(); - }); - }); - it("destroy doesn't destroy resources when they're in use", function () { return Promise.all([ loadAndZoomToModelExperimental({ gltf: boxTexturedGlbUrl }, scene), From 8016f62169e1a00fced55650a9b8888403db1c91 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 25 Jul 2022 17:32:34 -0400 Subject: [PATCH 02/10] Add missing unit tests --- .../ModelExperimentalSpec.js | 254 ++++++++++++++---- 1 file changed, 195 insertions(+), 59 deletions(-) diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index d044444b5f16..57b7c1e67707 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -7,6 +7,7 @@ import { ClippingPlane, ClippingPlaneCollection, Color, + ColorBlendMode, Credit, defaultValue, defined, @@ -30,6 +31,8 @@ import { Resource, ResourceCache, ShaderProgram, + ShadowMode, + SplitDirection, StyleCommandsNeeded, Transforms, WireframeIndexGenerator, @@ -102,7 +105,6 @@ describe( let scene; let scene2D; let sceneCV; - let sceneWithMockGlobe; let sceneWithWebgl2; beforeAll(function () { @@ -114,8 +116,6 @@ describe( sceneCV = createScene(); sceneCV.morphToColumbusView(0.0); - sceneWithMockGlobe = createScene(); - sceneWithWebgl2 = createScene({ contextOptions: { requestWebgl2: true, @@ -123,15 +123,10 @@ describe( }); }); - beforeEach(function () { - sceneWithMockGlobe.globe = createMockGlobe(); - }); - afterAll(function () { scene.destroyForSpecs(); scene2D.destroyForSpecs(); sceneCV.destroyForSpecs(); - sceneWithMockGlobe.destroyForSpecs(); sceneWithWebgl2.destroyForSpecs(); }); @@ -139,7 +134,6 @@ describe( scene.primitives.removeAll(); scene2D.primitives.removeAll(); sceneCV.primitives.removeAll(); - sceneWithMockGlobe.primitives.removeAll(); sceneWithWebgl2.primitives.removeAll(); ResourceCache.clearForSpecs(); }); @@ -243,55 +237,6 @@ describe( } } - function createMockGlobe() { - const globe = { - callback: undefined, - removedCallback: false, - ellipsoid: Ellipsoid.WGS84, - update: function () {}, - render: function () {}, - getHeight: function () { - return 0.0; - }, - _surface: { - tileProvider: { - ready: true, - }, - _tileLoadQueueHigh: [], - _tileLoadQueueMedium: [], - _tileLoadQueueLow: [], - _debug: { - tilesWaitingForChildren: 0, - }, - }, - imageryLayersUpdatedEvent: new Event(), - destroy: function () {}, - }; - - globe.beginFrame = function () {}; - - globe.endFrame = function () {}; - - globe.terrainProviderChanged = new Event(); - Object.defineProperties(globe, { - terrainProvider: { - set: function (value) { - this.terrainProviderChanged.raiseEvent(value); - }, - }, - }); - - globe._surface.updateHeight = function (position, callback) { - globe.callback = callback; - return function () { - globe.removedCallback = true; - globe.callback = undefined; - }; - }; - - return globe; - } - it("fromGltf throws with undefined options", function () { expect(function () { ModelExperimental.fromGltf(); @@ -497,6 +442,60 @@ describe( }); }); + it("sets default properties", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGlbUrl, + }, + scene + ).then(function (model) { + expect(model.show).toEqual(true); + expect(model.modelMatrix).toEqual(Matrix4.IDENTITY); + expect(model.scale).toEqual(1.0); + expect(model.minimumPixelSize).toEqual(0.0); + expect(model.maximumScale).toBeUndefined(); + + expect(model.id).toBeUndefined(); + expect(model.allowPicking).toEqual(true); + + expect(model.activeAnimations).toBeDefined(); + expect(model.clampAnimations).toEqual(true); + + expect(model.shadows).toEqual(ShadowMode.ENABLED); + expect(model.debugShowBoundingVolume).toEqual(false); + expect(model._enableDebugWireframe).toEqual(false); + expect(model.debugWireframe).toEqual(false); + + expect(model.cull).toEqual(true); + expect(model.backFaceCulling).toEqual(true); + expect(model.opaquePass).toEqual(Pass.OPAQUE); + expect(model.customShader).toBeUndefined(); + + expect(model.heightReference).toEqual(HeightReference.NONE); + expect(model.scene).toBeUndefined(); + expect(model.distanceDisplayCondition).toBeUndefined(); + + expect(model.color).toEqual(Color.WHITE); + expect(model.colorBlendMode).toEqual(ColorBlendMode.HIGHLIGHT); + expect(model.colorBlendAmount).toEqual(0.5); + expect(model.silhouetteColor).toEqual(Color.RED); + expect(model.silhouetteSize).toEqual(0.0); + + expect(model._enableShowOutline).toEqual(true); + expect(model.showOutline).toEqual(true); + expect(model.outlineColor).toEqual(Color.BLACK); + + expect(model.clippingPlanes).toBeUndefined(); + expect(model.lightColor).toBeUndefined(); + expect(model.imageBasedLighting).toBeDefined(); + + expect(model.credit).toBeUndefined(); + expect(model.showCreditsOnScreen).toEqual(false); + expect(model.splitDirection).toEqual(SplitDirection.NONE); + expect(model._projectTo2D).toEqual(false); + }); + }); + it("renders glTF with morph targets", function () { const resource = Resource.createIfNeeded(morphPrimitivesTestUrl); return resource.fetchJson().then(function (gltf) { @@ -1135,6 +1134,15 @@ describe( }); }); + it("boundingSphere throws if model is not ready", function () { + const model = ModelExperimental.fromGltf({ + url: boxTexturedGlbUrl, + }); + expect(function () { + return model.boundingSphere; + }).toThrowDeveloperError(); + }); + it("boundingSphere works", function () { const resource = Resource.createIfNeeded(boxTexturedGlbUrl); const loadPromise = resource.fetchArrayBuffer(); @@ -1588,6 +1596,73 @@ describe( }); describe("height reference", function () { + let sceneWithMockGlobe; + + function createMockGlobe() { + const globe = { + callback: undefined, + removedCallback: false, + ellipsoid: Ellipsoid.WGS84, + update: function () {}, + render: function () {}, + getHeight: function () { + return 0.0; + }, + _surface: { + tileProvider: { + ready: true, + }, + _tileLoadQueueHigh: [], + _tileLoadQueueMedium: [], + _tileLoadQueueLow: [], + _debug: { + tilesWaitingForChildren: 0, + }, + }, + imageryLayersUpdatedEvent: new Event(), + destroy: function () {}, + }; + + globe.beginFrame = function () {}; + + globe.endFrame = function () {}; + + globe.terrainProviderChanged = new Event(); + Object.defineProperties(globe, { + terrainProvider: { + set: function (value) { + this.terrainProviderChanged.raiseEvent(value); + }, + }, + }); + + globe._surface.updateHeight = function (position, callback) { + globe.callback = callback; + return function () { + globe.removedCallback = true; + globe.callback = undefined; + }; + }; + + return globe; + } + + beforeAll(function () { + sceneWithMockGlobe = createScene(); + }); + + beforeEach(function () { + sceneWithMockGlobe.globe = createMockGlobe(); + }); + + afterEach(function () { + sceneWithMockGlobe.primitives.removeAll(); + }); + + afterAll(function () { + sceneWithMockGlobe.destroyForSpecs(); + }); + it("initializes with height reference", function () { return loadAndZoomToModelExperimental( { @@ -1997,7 +2072,34 @@ describe( }); }); - it("invisible model doesn't render", function () { + it("renders with translucent color", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + let result; + expect(renderOptions).toRenderAndCall(function (rgba) { + result = rgba; + }); + + model.color = Color.fromAlpha(Color.WHITE, 0.5); + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba[0]).toBeLessThan(result[0]); + expect(rgba[1]).toBeLessThan(result[1]); + expect(rgba[2]).toBeLessThan(result[2]); + expect(rgba[3]).toBe(255); + }); + }); + }); + + it("doesn't render invisible model", function () { return loadAndZoomToModelExperimental( { gltf: boxTexturedGltfUrl, @@ -2319,6 +2421,40 @@ describe( }); }); }); + + it("changing imageBasedLighting parameters works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + imageBasedLighting: new ImageBasedLighting({ + imageBasedLightingFactor: Cartesian2.ZERO, + }), + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + let result; + verifyRender(model, true); + expect(renderOptions).toRenderAndCall(function (rgba) { + result = rgba; + }); + + const ibl = model.imageBasedLighting; + ibl.imageBasedLightingFactor = new Cartesian2(1, 1); + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(result); + }); + + ibl.luminanceAtZenith = 0.0; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).toEqual(result); + }); + }); + }); }); describe("scale", function () { From 8bab251d23d765e99d5f122eaca1129aece43759 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 26 Jul 2022 11:54:32 -0400 Subject: [PATCH 03/10] Add rendering tests --- .../ModelExperimentalSpec.js | 587 ++++++++++++++---- 1 file changed, 481 insertions(+), 106 deletions(-) diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index 57b7c1e67707..b80cff2bed4b 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -52,6 +52,13 @@ describe( "./Data/Models/GltfLoader/TriangleStrip/glTF/TriangleStrip.gltf"; const triangleFanUrl = "./Data/Models/GltfLoader/TriangleFan/glTF/TriangleFan.gltf"; + const animatedTriangleUrl = + "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; + const animatedTriangleOffset = new HeadingPitchRange( + CesiumMath.PI / 2.0, + 0, + 2.0 + ); const boxTexturedGlbUrl = "./Data/Models/GltfLoader/BoxTextured/glTF-Binary/BoxTextured.glb"; @@ -61,26 +68,24 @@ describe( "./Data/Models/GltfLoader/BoxTextured/glTF/BoxTextured.gltf"; const boxWithCreditsUrl = "./Data/Models/GltfLoader/BoxWithCopyright/glTF/Box.gltf"; - const microcosm = "./Data/Models/GltfLoader/Microcosm/glTF/microcosm.gltf"; const boxInstanced = "./Data/Models/GltfLoader/BoxInstanced/glTF/box-instanced.gltf"; const boxUnlitUrl = "./Data/Models/PBR/BoxUnlit/BoxUnlit.gltf"; const boxBackFaceCullingUrl = "./Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf"; const boxBackFaceCullingOffset = new HeadingPitchRange(Math.PI / 2, 0, 2.0); + const boxArticulationsUrl = + "./Data/Models/GltfLoader/BoxArticulations/glTF/BoxArticulations.gltf"; + + const microcosm = "./Data/Models/GltfLoader/Microcosm/glTF/microcosm.gltf"; const morphPrimitivesTestUrl = "./Data/Models/GltfLoader/MorphPrimitivesTest/glTF/MorphPrimitivesTest.gltf"; - const animatedTriangleUrl = - "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; - const animatedTriangleOffset = new HeadingPitchRange( - CesiumMath.PI / 2.0, - 0, - 2.0 - ); const pointCloudUrl = "./Data/Models/GltfLoader/PointCloudWithRGBColors/glTF-Binary/PointCloudWithRGBColors.glb"; - const boxArticulationsUrl = - "./Data/Models/GltfLoader/BoxArticulations/glTF/BoxArticulations.gltf"; + const twoSidedPlaneUrl = + "./Data/Models/GltfLoader/TwoSidedPlane/glTF/TwoSidedPlane.gltf"; + const riggedFigureUrl = + "./Data/Models/rigged-figure-test/rigged-figure-test.gltf"; // prettier-ignore const boxArticulationsMatrix = Matrix4.fromRowMajorArray([ @@ -265,34 +270,6 @@ describe( }); }); - it("initializes feature table", function () { - return loadAndZoomToModelExperimental( - { gltf: buildingsMetadata }, - scene - ).then(function (model) { - expect(model.ready).toEqual(true); - expect(model.featureTables).toBeDefined(); - - const featureTable = model.featureTables[0]; - expect(featureTable).toBeDefined(); - - const featuresLength = featureTable.featuresLength; - expect(featuresLength).toEqual(10); - expect(featureTable.batchTexture).toBeDefined(); - expect(featureTable.batchTexture._featuresLength).toEqual(10); - - for (let i = 0; i < featuresLength; i++) { - const 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 () { const resource = Resource.createIfNeeded(boxTexturedGltfUrl); return resource.fetchJson().then(function (gltf) { @@ -442,6 +419,34 @@ describe( }); }); + it("initializes feature table", function () { + return loadAndZoomToModelExperimental( + { gltf: buildingsMetadata }, + scene + ).then(function (model) { + expect(model.ready).toEqual(true); + expect(model.featureTables).toBeDefined(); + + const featureTable = model.featureTables[0]; + expect(featureTable).toBeDefined(); + + const featuresLength = featureTable.featuresLength; + expect(featuresLength).toEqual(10); + expect(featureTable.batchTexture).toBeDefined(); + expect(featureTable.batchTexture._featuresLength).toEqual(10); + + for (let i = 0; i < featuresLength; i++) { + const 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("sets default properties", function () { return loadAndZoomToModelExperimental( { @@ -496,7 +501,90 @@ describe( }); }); - it("renders glTF with morph targets", function () { + it("renders model without indices", function () { + const resource = Resource.createIfNeeded(triangleWithoutIndicesUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: triangleWithoutIndicesUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(0.0, 0.0, 100.0) + ), + }, + scene + ).then(function (model) { + // Orient the camera so it doesn't back-face cull the triangle. + const center = model.boundingSphere.center; + const range = 4.0 * model.boundingSphere.radius; + scene.camera.lookAt( + center, + new HeadingPitchRange(-CesiumMath.PI_OVER_TWO, 0, range) + ); + + // The triangle's diagonal edge is slightly out of frame. + scene.camera.moveDown(0.1); + + verifyRender(model, true, { + zoomToModel: false, + }); + }); + }); + }); + + it("renders model with double-sided material", function () { + const resource = Resource.createIfNeeded(twoSidedPlaneUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: twoSidedPlaneUrl, + modelMatrix: Transforms.eastNorthUpToFixedFrame( + Cartesian3.fromDegrees(0.0, 0.0, 100.0) + ), + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")), + }; + + const center = model.boundingSphere.center; + const range = 4.0 * model.boundingSphere.radius; + scene.camera.lookAt( + center, + new HeadingPitchRange(0, -CesiumMath.PI_OVER_TWO, range) + ); + + // The top of the double-sided plane should render brightly, since + // its normal is pointing towards the light + let result; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual([0, 0, 0, 255]); + result = rgba; + }); + + scene.camera.lookAt( + center, + new HeadingPitchRange(0, CesiumMath.PI_OVER_TWO, range) + ); + + // The bottom of the plane should render darker than the top, since + // its normal is pointing away from the light. + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual([0, 0, 0, 255]); + + expect(rgba[0]).toBeLessThan(result[0]); + expect(rgba[1]).toBeLessThan(result[1]); + expect(rgba[2]).toBeLessThan(result[2]); + expect(rgba[3]).toEqual(result[3]); + }); + }); + }); + }); + + it("renders model with morph targets", function () { const resource = Resource.createIfNeeded(morphPrimitivesTestUrl); return resource.fetchJson().then(function (gltf) { return loadAndZoomToModelExperimental( @@ -510,8 +598,12 @@ describe( // contains black, which can confuse the test. const renderOptions = { backgroundColor: Color.BLUE, + zoomToModel: false, }; + // Move the camera down slightly so the morphed part is in view. + scene.camera.moveDown(0.25); + // The model is a plane made three-dimensional by morph targets. // If morph targets aren't supported, the model won't appear in the camera. verifyRender(model, true, renderOptions); @@ -519,57 +611,59 @@ describe( }); }); - it("renders model without animations added", function () { - return loadAndZoomToModelExperimental( - { - gltf: animatedTriangleUrl, - offset: animatedTriangleOffset, - }, - scene - ).then(function (model) { - const animationCollection = model.activeAnimations; - expect(animationCollection).toBeDefined(); - expect(animationCollection.length).toBe(0); + describe("animations", function () { + it("renders model without animations added", function () { + return loadAndZoomToModelExperimental( + { + gltf: animatedTriangleUrl, + offset: animatedTriangleOffset, + }, + scene + ).then(function (model) { + const animationCollection = model.activeAnimations; + expect(animationCollection).toBeDefined(); + expect(animationCollection.length).toBe(0); - // Move camera so that the triangle is in view. - scene.camera.moveDown(0.5); - verifyRender(model, true, { - zoomToModel: false, + // Move camera so that the triangle is in view. + scene.camera.moveDown(0.5); + verifyRender(model, true, { + zoomToModel: false, + }); }); }); - }); - it("renders model with animations added", function () { - return loadAndZoomToModelExperimental( - { - gltf: animatedTriangleUrl, - offset: animatedTriangleOffset, - }, - scene - ).then(function (model) { - // Move camera so that the triangle is in view. - scene.camera.moveDown(0.5); + it("renders model with animations added", function () { + return loadAndZoomToModelExperimental( + { + gltf: animatedTriangleUrl, + offset: animatedTriangleOffset, + }, + scene + ).then(function (model) { + // Move camera so that the triangle is in view. + scene.camera.moveDown(0.5); - // The model rotates such that it leaves the view of the camera - // halfway into its animation. - const startTime = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); - const animationCollection = model.activeAnimations; - animationCollection.add({ - index: 0, - startTime: startTime, - }); - expect(animationCollection.length).toBe(1); - verifyRender(model, true, { - zoomToModel: false, - time: startTime, - }); + // The model rotates such that it leaves the view of the camera + // halfway into its animation. + const startTime = JulianDate.fromDate( + new Date("January 1, 2014 12:00:00 UTC") + ); + const animationCollection = model.activeAnimations; + animationCollection.add({ + index: 0, + startTime: startTime, + }); + expect(animationCollection.length).toBe(1); + verifyRender(model, true, { + zoomToModel: false, + time: startTime, + }); - const time = JulianDate.addSeconds(startTime, 0.5, new JulianDate()); - verifyRender(model, false, { - zoomToModel: false, - time: time, + const time = JulianDate.addSeconds(startTime, 0.5, new JulianDate()); + verifyRender(model, false, { + zoomToModel: false, + time: time, + }); }); }); }); @@ -1134,30 +1228,56 @@ describe( }); }); - it("boundingSphere throws if model is not ready", function () { - const model = ModelExperimental.fromGltf({ - url: boxTexturedGlbUrl, + describe("boundingSphere", function () { + it("boundingSphere throws if model is not ready", function () { + const model = ModelExperimental.fromGltf({ + url: boxTexturedGlbUrl, + }); + expect(function () { + return model.boundingSphere; + }).toThrowDeveloperError(); }); - expect(function () { - return model.boundingSphere; - }).toThrowDeveloperError(); - }); - it("boundingSphere works", function () { - const resource = Resource.createIfNeeded(boxTexturedGlbUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer) }, - scene - ).then(function (model) { - const boundingSphere = model.boundingSphere; - expect(boundingSphere).toBeDefined(); - expect(boundingSphere.center).toEqual(new Cartesian3()); - expect(boundingSphere.radius).toEqualEpsilon( - 0.8660254037844386, - CesiumMath.EPSILON8 - ); + it("boundingSphere works", function () { + const resource = Resource.createIfNeeded(boxTexturedGlbUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer) }, + scene + ).then(function (model) { + const boundingSphere = model.boundingSphere; + expect(boundingSphere).toBeDefined(); + expect(boundingSphere.center).toEqual(new Cartesian3()); + expect(boundingSphere.radius).toEqualEpsilon( + 0.8660254037844386, + CesiumMath.EPSILON8 + ); + }); + }); + }); + + it("boundingSphere accounts for axis correction", function () { + const resource = Resource.createIfNeeded(riggedFigureUrl); + const loadPromise = resource.fetchArrayBuffer(); + return loadPromise.then(function (buffer) { + return loadAndZoomToModelExperimental( + { gltf: new Uint8Array(buffer) }, + scene + ).then(function (model) { + // The bounding sphere should transform from z-forward + // to x-forward. + const boundingSphere = model.boundingSphere; + expect(boundingSphere).toBeDefined(); + expect(boundingSphere.center).toEqualEpsilon( + new Cartesian3(0.0320296511054039, 0, 0.7249599695205688), + CesiumMath.EPSILON3 + ); + expect(boundingSphere.radius).toEqualEpsilon( + 0.9484635280120018, + CesiumMath.EPSILON3 + ); + }); }); }); }); @@ -2116,6 +2236,211 @@ describe( }); }); + // These functions assume that model.color = Color.RED + function verifyHighlightColor(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + } + + function verifyReplaceColor(rgba) { + expect(rgba[0]).toEqual(255); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + } + + // Assumes colorBlendAmount = 0.5; + function verifyMixColor(rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[0]).toBeLessThan(255); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[1]).toBeLessThan(255); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[2]).toBeLessThan(255); + expect(rgba[3]).toEqual(255); + } + + describe("colorBlendMode", function () { + it("initializes with ColorBlendMode.HIGHLIGHT", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.RED, + colorBlendMode: ColorBlendMode.HIGHLIGHT, + }, + scene + ).then(function (model) { + expect(model.colorBlendMode).toEqual(ColorBlendMode.HIGHLIGHT); + + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyHighlightColor(rgba); + }); + }); + }); + + it("initializes with ColorBlendMode.REPLACE", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.RED, + colorBlendMode: ColorBlendMode.REPLACE, + }, + scene + ).then(function (model) { + expect(model.colorBlendMode).toEqual(ColorBlendMode.REPLACE); + + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyReplaceColor(rgba); + }); + }); + }); + + it("initializes with ColorBlendMode.MIX", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.RED, + colorBlendMode: ColorBlendMode.MIX, + }, + scene + ).then(function (model) { + expect(model.colorBlendMode).toEqual(ColorBlendMode.MIX); + + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyMixColor(rgba); + }); + }); + }); + + it("toggles colorBlendMode", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.RED, + colorBlendMode: ColorBlendMode.REPLACE, + }, + scene + ).then(function (model) { + expect(model.colorBlendMode).toEqual(ColorBlendMode.REPLACE); + + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyReplaceColor(rgba); + }); + + model.colorBlendMode = ColorBlendMode.HIGHLIGHT; + expect(model.colorBlendMode).toEqual(ColorBlendMode.HIGHLIGHT); + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyHighlightColor(rgba); + }); + + model.colorBlendMode = ColorBlendMode.MIX; + expect(model.colorBlendMode).toEqual(ColorBlendMode.MIX); + + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyMixColor(rgba); + }); + }); + }); + }); + + describe("colorBlendAmount", function () { + it("initializes with colorBlendAmount", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + color: Color.RED, + colorBlendMode: ColorBlendMode.MIX, + colorBlendAmount: 1.0, + }, + scene + ).then(function (model) { + expect(model.colorBlendAmount).toEqual(1.0); + + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + // colorBlendAmount = 1.0 is visually equivalent to + // ColorBlendMode.REPLACE + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyReplaceColor(rgba); + }); + }); + }); + + it("changing colorBlendAmount works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: new JulianDate(2456659.0004050927), + }; + + let originalColor; + expect(renderOptions).toRenderAndCall(function (rgba) { + originalColor = rgba; + }); + + model.color = Color.RED; + model.colorBlendMode = ColorBlendMode.MIX; + model.colorBlendAmount = 1.0; + expect(model.colorBlendAmount).toEqual(1.0); + + // colorBlendAmount = 1.0 is visually equivalent to + // ColorBlendMode.REPLACE + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyReplaceColor(rgba); + }); + + model.colorBlendAmount = 0.5; + expect(model.colorBlendAmount).toEqual(0.5); + expect(renderOptions).toRenderAndCall(function (rgba) { + verifyMixColor(rgba); + }); + + model.colorBlendAmount = 0.0; + expect(model.colorBlendAmount).toEqual(0.0); + // colorBlendAmount = 0.0 is visually equivalent to + // having no color applied to the model. + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba[0]).toEqual(originalColor[0]); + expect(rgba[1]).toEqual(originalColor[1]); + expect(rgba[2]).toEqual(originalColor[2]); + expect(rgba[3]).toEqual(originalColor[3]); + }); + }); + }); + }); + describe("silhouette", function () { it("initializes with silhouette size", function () { return loadAndZoomToModelExperimental( @@ -2781,6 +3106,56 @@ describe( }); }); + describe("cull", function () { + it("enables culling", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + cull: true, + }, + scene + ).then(function (model) { + expect(model.cull).toEqual(true); + + // Commands should be submitted while viewing the model. + scene.renderForSpecs(); + expect(scene.frustumCommandsList.length).toBeGreaterThan(0); + + // Commands should not be submitted when model is out of view. + model.modelMatrix = Matrix4.fromTranslation( + new Cartesian3(100.0, 0.0, 0.0) + ); + scene.renderForSpecs(); + expect(scene.frustumCommandsList.length).toEqual(0); + }); + }); + + // This test passes for Model but fails for ModelExperimental. + xit("disables culling", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + cull: false, + }, + scene + ).then(function (model) { + expect(model.cull).toEqual(false); + + // Commands should be submitted while viewing the model. + scene.renderForSpecs(); + const length = scene.frustumCommandsList.length; + expect(length).toBeGreaterThan(0); + + // Commands should still be submitted when model is out of view. + model.modelMatrix = Matrix4.fromTranslation( + new Cartesian3(100.0, 0.0, 0.0) + ); + scene.renderForSpecs(); + expect(scene.frustumCommandsList.length).toEqual(length); + }); + }); + }); + describe("back-face culling", function () { it("enables back-face culling", function () { return loadAndZoomToModelExperimental( From d74e4de9e29129e576d5d891f829cdcc1980bcd6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 26 Jul 2022 14:10:49 -0400 Subject: [PATCH 04/10] Add Draco and other loading tests --- Specs/Scene/GltfLoaderSpec.js | 46 +++ .../ModelExperimentalSpec.js | 314 ++++++++++++++---- 2 files changed, 302 insertions(+), 58 deletions(-) diff --git a/Specs/Scene/GltfLoaderSpec.js b/Specs/Scene/GltfLoaderSpec.js index d12aa232e00f..c7ed3218e6bd 100644 --- a/Specs/Scene/GltfLoaderSpec.js +++ b/Specs/Scene/GltfLoaderSpec.js @@ -113,6 +113,7 @@ describe( "./Data/Models/GltfLoader/BoxWithPrimitiveOutline/glTF/BoxWithPrimitiveOutline.gltf"; const boxWithPrimitiveOutlineSharedVertices = "./Data/Models/GltfLoader/BoxWithPrimitiveOutlineSharedVertices/glTF/BoxWithPrimitiveOutlineSharedVertices.gltf"; + const multiUvTest = "./Data/Models/MultiUVTest/MultiUVTest.glb"; let scene; let sceneWithWebgl2; @@ -1068,6 +1069,51 @@ describe( }); }); + it("loads MultiUVTest", function () { + return loadGltf(multiUvTest).then(function (gltfLoader) { + const components = gltfLoader.components; + const scene = components.scene; + const rootNode = scene.nodes[0]; + const primitive = rootNode.primitives[0]; + const material = primitive.material; + const baseColorTexture = material.metallicRoughness.baseColorTexture; + const emissiveTexture = material.emissiveTexture; + + const attributes = primitive.attributes; + const positionAttribute = getAttribute( + attributes, + VertexAttributeSemantic.POSITION + ); + const normalAttribute = getAttribute( + attributes, + VertexAttributeSemantic.NORMAL + ); + const tangentAttribute = getAttribute( + attributes, + VertexAttributeSemantic.TANGENT + ); + const texcoordAttribute0 = getAttribute( + attributes, + VertexAttributeSemantic.TEXCOORD, + 0 + ); + const texcoordAttribute1 = getAttribute( + attributes, + VertexAttributeSemantic.TEXCOORD, + 1 + ); + + expect(positionAttribute).toBeDefined(); + expect(normalAttribute).toBeDefined(); + expect(tangentAttribute).toBeDefined(); + expect(texcoordAttribute0).toBeDefined(); + expect(texcoordAttribute1).toBeDefined(); + + expect(baseColorTexture.texCoord).toBe(0); + expect(emissiveTexture.texCoord).toBe(1); + }); + }); + it("loads Microcosm", function () { return loadGltf(microcosm).then(function (gltfLoader) { const components = gltfLoader.components; diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index b80cff2bed4b..9eefdeeacce1 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -12,6 +12,7 @@ import { defaultValue, defined, DistanceDisplayCondition, + DracoLoader, Ellipsoid, Event, FeatureDetection, @@ -48,34 +49,26 @@ describe( const triangleWithoutIndicesUrl = "./Data/Models/GltfLoader/TriangleWithoutIndices/glTF/TriangleWithoutIndices.gltf"; - const triangleStripUrl = - "./Data/Models/GltfLoader/TriangleStrip/glTF/TriangleStrip.gltf"; - const triangleFanUrl = - "./Data/Models/GltfLoader/TriangleFan/glTF/TriangleFan.gltf"; - const animatedTriangleUrl = - "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; - const animatedTriangleOffset = new HeadingPitchRange( - CesiumMath.PI / 2.0, - 0, - 2.0 - ); + const boxTexturedGltfUrl = + "./Data/Models/GltfLoader/BoxTextured/glTF/BoxTextured.gltf"; const boxTexturedGlbUrl = "./Data/Models/GltfLoader/BoxTextured/glTF-Binary/BoxTextured.glb"; const buildingsMetadata = "./Data/Models/GltfLoader/BuildingsMetadata/glTF/buildings-metadata.gltf"; - const boxTexturedGltfUrl = - "./Data/Models/GltfLoader/BoxTextured/glTF/BoxTextured.gltf"; - const boxWithCreditsUrl = - "./Data/Models/GltfLoader/BoxWithCopyright/glTF/Box.gltf"; + const boxInstanced = "./Data/Models/GltfLoader/BoxInstanced/glTF/box-instanced.gltf"; const boxUnlitUrl = "./Data/Models/PBR/BoxUnlit/BoxUnlit.gltf"; - const boxBackFaceCullingUrl = - "./Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf"; - const boxBackFaceCullingOffset = new HeadingPitchRange(Math.PI / 2, 0, 2.0); const boxArticulationsUrl = "./Data/Models/GltfLoader/BoxArticulations/glTF/BoxArticulations.gltf"; + // prettier-ignore + const boxArticulationsMatrix = Matrix4.fromRowMajorArray([ + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1 + ]); const microcosm = "./Data/Models/GltfLoader/Microcosm/glTF/microcosm.gltf"; const morphPrimitivesTestUrl = @@ -84,16 +77,13 @@ describe( "./Data/Models/GltfLoader/PointCloudWithRGBColors/glTF-Binary/PointCloudWithRGBColors.glb"; const twoSidedPlaneUrl = "./Data/Models/GltfLoader/TwoSidedPlane/glTF/TwoSidedPlane.gltf"; + const vertexColorTestUrl = + "./Data/Models/PBR/VertexColorTest/VertexColorTest.gltf"; + const emissiveTextureUrl = "./Data/Models/PBR/BoxEmissive/BoxEmissive.gltf"; const riggedFigureUrl = "./Data/Models/rigged-figure-test/rigged-figure-test.gltf"; - - // prettier-ignore - const boxArticulationsMatrix = Matrix4.fromRowMajorArray([ - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1 - ]); + const dracoCesiumManUrl = + "./Data/Models/DracoCompression/CesiumMan/CesiumMan.gltf"; const fixedFrameTransform = Transforms.localFrameToFixedFrameGenerator( "north", @@ -110,7 +100,6 @@ describe( let scene; let scene2D; let sceneCV; - let sceneWithWebgl2; beforeAll(function () { scene = createScene(); @@ -120,26 +109,18 @@ describe( sceneCV = createScene(); sceneCV.morphToColumbusView(0.0); - - sceneWithWebgl2 = createScene({ - contextOptions: { - requestWebgl2: true, - }, - }); }); afterAll(function () { scene.destroyForSpecs(); scene2D.destroyForSpecs(); sceneCV.destroyForSpecs(); - sceneWithWebgl2.destroyForSpecs(); }); afterEach(function () { scene.primitives.removeAll(); scene2D.primitives.removeAll(); sceneCV.primitives.removeAll(); - sceneWithWebgl2.primitives.removeAll(); ResourceCache.clearForSpecs(); }); @@ -154,6 +135,9 @@ describe( } const scratchBytes = []; + const defaultDate = JulianDate.fromDate( + new Date("January 1, 2014 12:00:00 UTC") + ); function verifyRender(model, shouldRender, options) { expect(model.ready).toBe(true); @@ -174,10 +158,7 @@ describe( targetScene.backgroundColor = backgroundColor; const backgroundColorBytes = backgroundColor.toBytes(scratchBytes); - const time = defaultValue( - options.time, - JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")) - ); + const time = defaultValue(options.time, defaultDate); expect({ scene: targetScene, @@ -532,6 +513,42 @@ describe( }); }); + it("renders model with vertex colors", function () { + const resource = Resource.createIfNeeded(vertexColorTestUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: vertexColorTestUrl, + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: defaultDate, + }; + // Move the camera to the blue plane. + scene.camera.moveLeft(0.5); + + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba[0]).toEqual(0); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(255); + expect(rgba[3]).toEqual(255); + }); + + // Move the camera to the red plane. + scene.camera.moveRight(1.0); + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba[0]).toEqual(255); + expect(rgba[1]).toEqual(0); + expect(rgba[2]).toEqual(0); + expect(rgba[3]).toEqual(255); + }); + }); + }); + }); + it("renders model with double-sided material", function () { const resource = Resource.createIfNeeded(twoSidedPlaneUrl); return resource.fetchJson().then(function (gltf) { @@ -547,7 +564,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")), + time: defaultDate, }; const center = model.boundingSphere.center; @@ -584,6 +601,33 @@ describe( }); }); + // This test works for Model but does not work for ModelExperimental. + xit("renders model with emissive texture", function () { + const resource = Resource.createIfNeeded(emissiveTextureUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: emissiveTextureUrl, + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: defaultDate, + }; + + expect(renderOptions).toRenderAndCall(function (rgba) { + // Emissive texture is red + expect(rgba[0]).toBeGreaterThan(20); + expect(rgba[1]).toBeLessThan(20); + expect(rgba[2]).toBeLessThan(20); + expect(rgba[3]).toEqual(255); + }); + }); + }); + }); + it("renders model with morph targets", function () { const resource = Resource.createIfNeeded(morphPrimitivesTestUrl); return resource.fetchJson().then(function (gltf) { @@ -611,7 +655,66 @@ describe( }); }); + it("renders Draco-compressed model", function () { + return loadAndZoomToModelExperimental( + { gltf: dracoCesiumManUrl }, + scene + ).then(function (model) { + verifyRender(model, true); + }); + }); + + it("fails to load with Draco decoding error", function () { + const readyPromise = pollToPromise(function () { + return DracoLoader._taskProcessorReady; + }); + DracoLoader._getDecoderTaskProcessor(); + return readyPromise + .then(function () { + const decoder = DracoLoader._getDecoderTaskProcessor(); + spyOn(decoder, "scheduleTask").and.callFake(function () { + return Promise.reject({ message: "Custom error" }); + }); + + const model = scene.primitives.add( + ModelExperimental.fromGltf({ + url: dracoCesiumManUrl, + }) + ); + + return Promise.all([ + pollToPromise( + function () { + scene.renderForSpecs(); + return model.loader._state === 7; // FAILED + }, + { timeout: 10000 } + ), + model.readyPromise, + ]); + }) + .then(function (e) { + fail("Should not resolve"); + }) + .catch(function (e) { + expect(e).toBeDefined(); + expect( + e.message.includes(`Failed to load model: ${dracoCesiumManUrl}`) + ).toBe(true); + expect(e.message.includes(`Failed to load Draco`)).toBe(true); + expect(e.message.includes(`Custom error`)).toBe(true); + }); + }); + describe("animations", function () { + const animatedTriangleUrl = + "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; + const animatedTriangleOffset = new HeadingPitchRange( + CesiumMath.PI / 2.0, + 0, + 2.0 + ); + it("renders model without animations added", function () { return loadAndZoomToModelExperimental( { @@ -645,9 +748,7 @@ describe( // The model rotates such that it leaves the view of the camera // halfway into its animation. - const startTime = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const startTime = defaultDate; const animationCollection = model.activeAnimations; animationCollection.add({ index: 0, @@ -666,6 +767,22 @@ describe( }); }); }); + + it("adds animation to draco-compressed model", function () { + return loadAndZoomToModelExperimental( + { gltf: dracoCesiumManUrl }, + scene + ).then(function (model) { + verifyRender(model, true); + + const animationCollection = model.activeAnimations; + const animation = animationCollection.add({ + index: 0, + }); + expect(animation).toBeDefined(); + expect(animationCollection.length).toBe(1); + }); + }); }); it("show works", function () { @@ -679,6 +796,7 @@ describe( expect(model.ready).toEqual(true); expect(model.show).toEqual(false); verifyRender(model, false); + model.show = true; expect(model.show).toEqual(true); verifyRender(model, true); @@ -864,6 +982,9 @@ describe( }); describe("credits", function () { + const boxWithCreditsUrl = + "./Data/Models/GltfLoader/BoxWithCopyright/glTF/Box.gltf"; + it("initializes with credit", function () { const credit = new Credit("User Credit"); const resource = Resource.createIfNeeded(boxTexturedGltfUrl); @@ -1089,6 +1210,29 @@ describe( }); describe("debugWireframe", function () { + const triangleStripUrl = + "./Data/Models/GltfLoader/TriangleStrip/glTF/TriangleStrip.gltf"; + const triangleFanUrl = + "./Data/Models/GltfLoader/TriangleFan/glTF/TriangleFan.gltf"; + + let sceneWithWebgl2; + + beforeAll(function () { + sceneWithWebgl2 = createScene({ + contextOptions: { + requestWebgl2: true, + }, + }); + }); + + afterEach(function () { + sceneWithWebgl2.primitives.removeAll(); + }); + + afterAll(function () { + sceneWithWebgl2.destroyForSpecs(); + }); + it("debugWireframe works for WebGL1 if enableDebugWireframe is true", function () { const resource = Resource.createIfNeeded(boxTexturedGlbUrl); const loadPromise = resource.fetchArrayBuffer(); @@ -2201,7 +2345,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; let result; @@ -2277,9 +2421,8 @@ describe( const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; - expect(renderOptions).toRenderAndCall(function (rgba) { verifyHighlightColor(rgba); }); @@ -2299,9 +2442,8 @@ describe( const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; - expect(renderOptions).toRenderAndCall(function (rgba) { verifyReplaceColor(rgba); }); @@ -2321,9 +2463,8 @@ describe( const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; - expect(renderOptions).toRenderAndCall(function (rgba) { verifyMixColor(rgba); }); @@ -2343,9 +2484,8 @@ describe( const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; - expect(renderOptions).toRenderAndCall(function (rgba) { verifyReplaceColor(rgba); }); @@ -2382,9 +2522,8 @@ describe( const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; - // colorBlendAmount = 1.0 is visually equivalent to // ColorBlendMode.REPLACE expect(renderOptions).toRenderAndCall(function (rgba) { @@ -2402,7 +2541,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; let originalColor; @@ -2731,7 +2870,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; let result; @@ -2759,7 +2898,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; let result; @@ -3157,6 +3296,14 @@ describe( }); describe("back-face culling", function () { + const boxBackFaceCullingUrl = + "./Data/Models/Box-Back-Face-Culling/Box-Back-Face-Culling.gltf"; + const boxBackFaceCullingOffset = new HeadingPitchRange( + Math.PI / 2, + 0, + 2.0 + ); + it("enables back-face culling", function () { return loadAndZoomToModelExperimental( { @@ -3268,7 +3415,7 @@ describe( ).then(function (model) { const renderOptions = { scene: scene, - time: new JulianDate(2456659.0004050927), + time: defaultDate, }; // The model should look the same whether it has -1.0 scale or 1.0 scale. @@ -3496,6 +3643,57 @@ describe( }); }); + describe("statistics", function () { + it("gets triangle count", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl }, + scene + ).then(function (model) { + const statistics = model.statistics; + expect(statistics.trianglesLength).toEqual(12); + }); + }); + + it("gets point count", function () { + return loadAndZoomToModelExperimental( + { gltf: pointCloudUrl }, + scene + ).then(function (model) { + const statistics = model.statistics; + expect(statistics.pointsLength).toEqual(2500); + }); + }); + + it("gets memory usage for geometry and textures", function () { + return loadAndZoomToModelExperimental( + { gltf: boxTexturedGltfUrl, incrementallyLoadTextures: false }, + scene + ).then(function (model) { + const expectedGeometryMemory = 840; + // Texture is 256*256 and then is mipmapped + const expectedTextureMemory = Math.floor(256 * 256 * 4 * (4 / 3)); + + const statistics = model.statistics; + expect(statistics.geometryByteLength).toEqual(expectedGeometryMemory); + expect(statistics.texturesByteLength).toEqual(expectedTextureMemory); + }); + }); + + it("gets memory usage for property tables", function () { + return loadAndZoomToModelExperimental( + { gltf: buildingsMetadata }, + scene + ).then(function (model) { + const expectedPropertyTableMemory = 110; + + const statistics = model.statistics; + expect(statistics.propertyTablesByteLength).toEqual( + expectedPropertyTableMemory + ); + }); + }); + }); + describe("AGI_articulations", function () { it("setArticulationStage throws when model is not ready", function () { const model = ModelExperimental.fromGltf({ From d1a4dcbf2137bd0900ce4db3d20e993ad2690165 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 26 Jul 2022 17:18:24 -0400 Subject: [PATCH 05/10] Change animation specs --- .../ModelAnimationChannelSpec.js | 2 +- ...odelExperimentalAnimationCollectionSpec.js | 67 +++++---- .../ModelExperimentalSpec.js | 129 +++++++++--------- 3 files changed, 104 insertions(+), 94 deletions(-) diff --git a/Specs/Scene/ModelExperimental/ModelAnimationChannelSpec.js b/Specs/Scene/ModelExperimental/ModelAnimationChannelSpec.js index 90f53096a545..c14996374f43 100644 --- a/Specs/Scene/ModelExperimental/ModelAnimationChannelSpec.js +++ b/Specs/Scene/ModelExperimental/ModelAnimationChannelSpec.js @@ -589,7 +589,7 @@ describe("Scene/ModelExperimental/ModelAnimationChannel", function () { const wrappedRuntimeAnimation = { model: { - clampedAnimations: false, + clampAnimations: false, }, }; diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js index 4b597e9c797e..09726cc91111 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js @@ -16,6 +16,9 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio const interpolationTestUrl = "./Data/Models/InterpolationTest/InterpolationTest.glb"; + const defaultDate = JulianDate.fromDate( + new Date("January 1, 2014 12:00:00 UTC") + ); const scratchJulianDate = new JulianDate(); let scene; @@ -475,7 +478,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - let time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC")); + let time = defaultDate; const animations = model.activeAnimations; const animation = animations.add({ index: 0, @@ -520,17 +523,42 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }); }); - it("animates with a delay", function () { + it("finishes animation when it reaches its end", function () { return loadAndZoomToModelExperimental( { gltf: animatedTriangleUrl, }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; + const animationCollection = model.activeAnimations; + const animation = animationCollection.add({ + index: 0, + }); + + const spyUpdate = jasmine.createSpy("listener"); + animation.update.addEventListener(spyUpdate); + scene.renderForSpecs(time); + scene.renderForSpecs(JulianDate.addSeconds(time, 1.0, scratchJulianDate)); + scene.renderForSpecs(JulianDate.addSeconds(time, 2.0, scratchJulianDate)); + + expect(spyUpdate.calls.count()).toEqual(3); + expect(spyUpdate.calls.argsFor(0)[2]).toEqual(0.0); + expect(spyUpdate.calls.argsFor(1)[2]).toEqual(1.0); + // Animation has reached its final time value. + expect(spyUpdate.calls.argsFor(2)[2]).toEqual(1.0); + }); + }); + + it("animates with a delay", function () { + return loadAndZoomToModelExperimental( + { + gltf: animatedTriangleUrl, + }, + scene + ).then(function (model) { + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, @@ -555,10 +583,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); - + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, @@ -623,9 +648,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; let animationTime = 0; const animation = animationCollection.add({ @@ -672,9 +695,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; animationCollection.animateWhilePaused = true; let animationTime = 0; @@ -719,9 +740,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, @@ -750,9 +769,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, @@ -781,9 +798,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, @@ -814,9 +829,7 @@ describe("Scene/ModelExperimental/ModelExperimentalAnimationCollection", functio }, scene ).then(function (model) { - const time = JulianDate.fromDate( - new Date("January 1, 2014 12:00:00 UTC") - ); + const time = defaultDate; const animationCollection = model.activeAnimations; const animation = animationCollection.add({ index: 0, diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index 9eefdeeacce1..945883058652 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -49,6 +49,13 @@ describe( const triangleWithoutIndicesUrl = "./Data/Models/GltfLoader/TriangleWithoutIndices/glTF/TriangleWithoutIndices.gltf"; + const animatedTriangleUrl = + "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; + const animatedTriangleOffset = new HeadingPitchRange( + CesiumMath.PI / 2.0, + 0, + 2.0 + ); const boxTexturedGltfUrl = "./Data/Models/GltfLoader/BoxTextured/glTF/BoxTextured.gltf"; @@ -706,82 +713,72 @@ describe( }); }); - describe("animations", function () { - const animatedTriangleUrl = - "./Data/Models/GltfLoader/AnimatedTriangle/glTF/AnimatedTriangle.gltf"; - const animatedTriangleOffset = new HeadingPitchRange( - CesiumMath.PI / 2.0, - 0, - 2.0 - ); - - it("renders model without animations added", function () { - return loadAndZoomToModelExperimental( - { - gltf: animatedTriangleUrl, - offset: animatedTriangleOffset, - }, - scene - ).then(function (model) { - const animationCollection = model.activeAnimations; - expect(animationCollection).toBeDefined(); - expect(animationCollection.length).toBe(0); + it("renders model without animations added", function () { + return loadAndZoomToModelExperimental( + { + gltf: animatedTriangleUrl, + offset: animatedTriangleOffset, + }, + scene + ).then(function (model) { + const animationCollection = model.activeAnimations; + expect(animationCollection).toBeDefined(); + expect(animationCollection.length).toBe(0); - // Move camera so that the triangle is in view. - scene.camera.moveDown(0.5); - verifyRender(model, true, { - zoomToModel: false, - }); + // Move camera so that the triangle is in view. + scene.camera.moveDown(0.5); + verifyRender(model, true, { + zoomToModel: false, }); }); + }); - it("renders model with animations added", function () { - return loadAndZoomToModelExperimental( - { - gltf: animatedTriangleUrl, - offset: animatedTriangleOffset, - }, - scene - ).then(function (model) { - // Move camera so that the triangle is in view. - scene.camera.moveDown(0.5); - - // The model rotates such that it leaves the view of the camera - // halfway into its animation. - const startTime = defaultDate; - const animationCollection = model.activeAnimations; - animationCollection.add({ - index: 0, - startTime: startTime, - }); - expect(animationCollection.length).toBe(1); - verifyRender(model, true, { - zoomToModel: false, - time: startTime, - }); + it("renders model with animations added", function () { + return loadAndZoomToModelExperimental( + { + gltf: animatedTriangleUrl, + offset: animatedTriangleOffset, + }, + scene + ).then(function (model) { + // Move camera so that the triangle is in view. + scene.camera.moveDown(0.5); + + // The model rotates such that it leaves the view of the camera + // halfway into its animation. + const startTime = defaultDate; + const animationCollection = model.activeAnimations; + animationCollection.add({ + index: 0, + startTime: startTime, + }); + expect(animationCollection.length).toBe(1); + verifyRender(model, true, { + zoomToModel: false, + time: startTime, + }); - const time = JulianDate.addSeconds(startTime, 0.5, new JulianDate()); - verifyRender(model, false, { - zoomToModel: false, - time: time, - }); + const time = JulianDate.addSeconds(startTime, 0.5, new JulianDate()); + verifyRender(model, false, { + zoomToModel: false, + time: time, }); }); + }); - it("adds animation to draco-compressed model", function () { - return loadAndZoomToModelExperimental( - { gltf: dracoCesiumManUrl }, - scene - ).then(function (model) { - verifyRender(model, true); + it("adds animation to draco-compressed model", function () { + return loadAndZoomToModelExperimental( + { gltf: dracoCesiumManUrl }, + scene + ).then(function (model) { + verifyRender(model, true); - const animationCollection = model.activeAnimations; - const animation = animationCollection.add({ - index: 0, - }); - expect(animation).toBeDefined(); - expect(animationCollection.length).toBe(1); + const animationCollection = model.activeAnimations; + const animation = animationCollection.add({ + index: 0, }); + expect(animation).toBeDefined(); + expect(animationCollection.length).toBe(1); }); }); From b31cc09f13ab4512804741f358fba80bd1f85907 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 27 Jul 2022 10:04:27 -0400 Subject: [PATCH 06/10] ModelExperimentalDrawCommand -> ModelDrawCommand --- Specs/Scene/ModelExperimental/ModelMatrixUpdateStageSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Specs/Scene/ModelExperimental/ModelMatrixUpdateStageSpec.js b/Specs/Scene/ModelExperimental/ModelMatrixUpdateStageSpec.js index 0fb449aed242..faa92da94922 100644 --- a/Specs/Scene/ModelExperimental/ModelMatrixUpdateStageSpec.js +++ b/Specs/Scene/ModelExperimental/ModelMatrixUpdateStageSpec.js @@ -5,7 +5,7 @@ import { CullFace, Matrix4, Math as CesiumMath, - ModelExperimentalDrawCommand, + ModelDrawCommand, ResourceCache, Quaternion, } from "../../../Source/Cesium.js"; @@ -88,7 +88,7 @@ describe( rootDrawCommand.boundingVolume = new BoundingSphere(); rootNode.runtimePrimitives.push({ updateStages: [], - drawCommand: new ModelExperimentalDrawCommand({ + drawCommand: new ModelDrawCommand({ command: rootDrawCommand, primitiveRenderResources: renderResources, }), @@ -101,7 +101,7 @@ describe( leafDrawCommand.boundingVolume = new BoundingSphere(); leafNode.runtimePrimitives.push({ updateStages: [], - drawCommand: new ModelExperimentalDrawCommand({ + drawCommand: new ModelDrawCommand({ command: leafDrawCommand, primitiveRenderResources: renderResources, }), From 74fda465b17e1d3ef45bba5ce2e6cd5c2b5e749e Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 27 Jul 2022 14:38:01 -0400 Subject: [PATCH 07/10] Fix loader tests --- Source/Scene/GltfJsonLoader.js | 40 ------------------- Source/Scene/GltfLoader.js | 16 +++++++- .../ModelExperimental/ModelExperimental.js | 4 +- Specs/Scene/GltfLoaderSpec.js | 23 +++++++++++ .../ModelExperimentalSpec.js | 15 +++++++ 5 files changed, 56 insertions(+), 42 deletions(-) diff --git a/Source/Scene/GltfJsonLoader.js b/Source/Scene/GltfJsonLoader.js index 9f181978be28..d2de87ce1904 100644 --- a/Source/Scene/GltfJsonLoader.js +++ b/Source/Scene/GltfJsonLoader.js @@ -10,7 +10,6 @@ import addPipelineExtras from "./GltfPipeline/addPipelineExtras.js"; import ForEach from "./GltfPipeline/ForEach.js"; import parseGlb from "./GltfPipeline/parseGlb.js"; import removePipelineExtras from "./GltfPipeline/removePipelineExtras.js"; -import updateVersion from "./GltfPipeline/updateVersion.js"; import ResourceLoader from "./ResourceLoader.js"; import ResourceLoaderState from "./ResourceLoaderState.js"; @@ -165,42 +164,6 @@ function handleError(gltfJsonLoader, error) { return Promise.reject(gltfJsonLoader.getError(errorMessage, error)); } -function upgradeVersion(gltfJsonLoader, gltf) { - if (gltf.asset.version === "2.0") { - return Promise.resolve(); - } - - // Load all buffers into memory. updateVersion will read and in some cases modify - // the buffer data, which it accesses from buffer.extras._pipeline.source - const promises = []; - ForEach.buffer(gltf, function (buffer) { - if ( - !defined(buffer.extras._pipeline.source) && // Ignore uri if this buffer uses the glTF 1.0 KHR_binary_glTF extension - defined(buffer.uri) - ) { - const resource = gltfJsonLoader._baseResource.getDerivedResource({ - url: buffer.uri, - }); - const resourceCache = gltfJsonLoader._resourceCache; - const bufferLoader = resourceCache.loadExternalBuffer({ - resource: resource, - }); - - gltfJsonLoader._bufferLoaders.push(bufferLoader); - - promises.push( - bufferLoader.promise.then(function (bufferLoader) { - buffer.extras._pipeline.source = bufferLoader.typedArray; - }) - ); - } - }); - - return Promise.all(promises).then(function () { - updateVersion(gltf); - }); -} - function decodeDataUris(gltf) { const promises = []; ForEach.buffer(gltf, function (buffer) { @@ -244,9 +207,6 @@ function processGltfJson(gltfJsonLoader, gltf) { addPipelineExtras(gltf); return decodeDataUris(gltf) - .then(function () { - return upgradeVersion(gltfJsonLoader, gltf); - }) .then(function () { addDefaults(gltf); return loadEmbeddedBuffers(gltfJsonLoader, gltf); diff --git a/Source/Scene/GltfLoader.js b/Source/Scene/GltfLoader.js index 5ecbfba99f25..f44729af2833 100644 --- a/Source/Scene/GltfLoader.js +++ b/Source/Scene/GltfLoader.js @@ -11,6 +11,7 @@ import defined from "../Core/defined.js"; import FeatureDetection from "../Core/FeatureDetection.js"; import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js"; import getComponentReader from "./GltfPipeline/getComponentReader.js"; +import getMagic from "../Core/getMagic.js"; import GltfLoaderUtil from "./GltfLoaderUtil.js"; import GltfStructuralMetadataLoader from "./GltfStructuralMetadataLoader.js"; import InstanceAttributeSemantic from "./InstanceAttributeSemantic.js"; @@ -327,6 +328,16 @@ Object.defineProperties(GltfLoader.prototype, { * @private */ GltfLoader.prototype.load = function () { + if (defined(this._typedArray)) { + const magic = getMagic(this._typedArray, this._typedArray.byteOffset); + if (magic !== "glTF") { + const url = this._gltfResource.url; + throw new RuntimeError( + `Failed to load ${url}: glb contains invalid magic header` + ); + } + } + const gltfJsonLoader = ResourceCache.loadGltfJson({ gltfResource: this._gltfResource, baseResource: this._baseResource, @@ -2196,7 +2207,10 @@ function parse( ) { const version = gltf.asset.version; if (version !== "2.0") { - throw new RuntimeError(`Unsupported glTF version: ${version}`); + const url = loader._gltfResource.url; + throw new RuntimeError( + `Failed to load ${url}: \nUnsupported glTF version: ${version}` + ); } const extensionsRequired = gltf.extensionsRequired; if (defined(extensionsRequired)) { diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 998fe42a314d..5726d012e966 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -373,6 +373,8 @@ export default function ModelExperimental(options) { this._sceneGraph = undefined; this._nodesByName = {}; // Stores the nodes by their names in the glTF. + + this._ignoreCommands = defaultValue(options.ignoreCommands, false); } function createModelFeatureTables(model, structuralMetadata) { @@ -2012,7 +2014,7 @@ function submitDrawCommands(model, frameState) { displayConditionPassed && (!invisible || silhouette); - if (showModel) { + if (showModel && !model._ignoreCommands) { addCreditsToCreditDisplay(model, frameState); const drawCommands = model._sceneGraph.getDrawCommands(frameState); frameState.commandList.push.apply(frameState.commandList, drawCommands); diff --git a/Specs/Scene/GltfLoaderSpec.js b/Specs/Scene/GltfLoaderSpec.js index c7ed3218e6bd..6d873f559816 100644 --- a/Specs/Scene/GltfLoaderSpec.js +++ b/Specs/Scene/GltfLoaderSpec.js @@ -177,6 +177,20 @@ describe( ); }); + it("throws if glb has invalid magic number", function () { + const gltfLoader = new GltfLoader({ + gltfResource: new Resource({ + url: boxTexturedBinary, + }), + incrementallyLoadTextures: false, + typedArray: new Uint8Array(20), + }); + + expect(function () { + gltfLoader.load(); + }).toThrowError(); + }); + function getOptions(gltfPath, options) { const resource = new Resource({ url: gltfPath, @@ -260,6 +274,15 @@ describe( return undefined; } + it("preserves query string in url", function () { + const params = "?param1=1¶m2=2"; + const url = boxTextured + params; + return loadGltf(url).then(function (gltfLoader) { + const loaderResource = gltfLoader._gltfResource; + expect(loaderResource.url).toEndWith(params); + }); + }); + it("loads BoxInterleaved", function () { return loadGltf(boxInterleaved).then(function (gltfLoader) { const components = gltfLoader.components; diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index 945883058652..a26c5adf128c 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -3242,6 +3242,21 @@ describe( }); }); + it("does not issue draw commands when ignoreCommands is true", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + }, + scene + ).then(function (model) { + expect(model.ready).toBe(true); + model._ignoreCommands = true; + + scene.renderForSpecs(); + expect(scene.frameState.commandList.length).toEqual(0); + }); + }); + describe("cull", function () { it("enables culling", function () { return loadAndZoomToModelExperimental( From 640b563444ff29897e9121870b3d41abf2cd5342 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 27 Jul 2022 15:41:52 -0400 Subject: [PATCH 08/10] Final test adjustments --- Source/Scene/GltfJsonLoader.js | 40 ++++ Source/Scene/GltfLoader.js | 11 - Specs/Scene/GltfLoaderSpec.js | 16 +- .../ModelExperimentalSpec.js | 210 ++++++++++++++++-- 4 files changed, 230 insertions(+), 47 deletions(-) diff --git a/Source/Scene/GltfJsonLoader.js b/Source/Scene/GltfJsonLoader.js index d2de87ce1904..9f181978be28 100644 --- a/Source/Scene/GltfJsonLoader.js +++ b/Source/Scene/GltfJsonLoader.js @@ -10,6 +10,7 @@ import addPipelineExtras from "./GltfPipeline/addPipelineExtras.js"; import ForEach from "./GltfPipeline/ForEach.js"; import parseGlb from "./GltfPipeline/parseGlb.js"; import removePipelineExtras from "./GltfPipeline/removePipelineExtras.js"; +import updateVersion from "./GltfPipeline/updateVersion.js"; import ResourceLoader from "./ResourceLoader.js"; import ResourceLoaderState from "./ResourceLoaderState.js"; @@ -164,6 +165,42 @@ function handleError(gltfJsonLoader, error) { return Promise.reject(gltfJsonLoader.getError(errorMessage, error)); } +function upgradeVersion(gltfJsonLoader, gltf) { + if (gltf.asset.version === "2.0") { + return Promise.resolve(); + } + + // Load all buffers into memory. updateVersion will read and in some cases modify + // the buffer data, which it accesses from buffer.extras._pipeline.source + const promises = []; + ForEach.buffer(gltf, function (buffer) { + if ( + !defined(buffer.extras._pipeline.source) && // Ignore uri if this buffer uses the glTF 1.0 KHR_binary_glTF extension + defined(buffer.uri) + ) { + const resource = gltfJsonLoader._baseResource.getDerivedResource({ + url: buffer.uri, + }); + const resourceCache = gltfJsonLoader._resourceCache; + const bufferLoader = resourceCache.loadExternalBuffer({ + resource: resource, + }); + + gltfJsonLoader._bufferLoaders.push(bufferLoader); + + promises.push( + bufferLoader.promise.then(function (bufferLoader) { + buffer.extras._pipeline.source = bufferLoader.typedArray; + }) + ); + } + }); + + return Promise.all(promises).then(function () { + updateVersion(gltf); + }); +} + function decodeDataUris(gltf) { const promises = []; ForEach.buffer(gltf, function (buffer) { @@ -207,6 +244,9 @@ function processGltfJson(gltfJsonLoader, gltf) { addPipelineExtras(gltf); return decodeDataUris(gltf) + .then(function () { + return upgradeVersion(gltfJsonLoader, gltf); + }) .then(function () { addDefaults(gltf); return loadEmbeddedBuffers(gltfJsonLoader, gltf); diff --git a/Source/Scene/GltfLoader.js b/Source/Scene/GltfLoader.js index f44729af2833..0b49f1baa511 100644 --- a/Source/Scene/GltfLoader.js +++ b/Source/Scene/GltfLoader.js @@ -11,7 +11,6 @@ import defined from "../Core/defined.js"; import FeatureDetection from "../Core/FeatureDetection.js"; import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js"; import getComponentReader from "./GltfPipeline/getComponentReader.js"; -import getMagic from "../Core/getMagic.js"; import GltfLoaderUtil from "./GltfLoaderUtil.js"; import GltfStructuralMetadataLoader from "./GltfStructuralMetadataLoader.js"; import InstanceAttributeSemantic from "./InstanceAttributeSemantic.js"; @@ -328,16 +327,6 @@ Object.defineProperties(GltfLoader.prototype, { * @private */ GltfLoader.prototype.load = function () { - if (defined(this._typedArray)) { - const magic = getMagic(this._typedArray, this._typedArray.byteOffset); - if (magic !== "glTF") { - const url = this._gltfResource.url; - throw new RuntimeError( - `Failed to load ${url}: glb contains invalid magic header` - ); - } - } - const gltfJsonLoader = ResourceCache.loadGltfJson({ gltfResource: this._gltfResource, baseResource: this._baseResource, diff --git a/Specs/Scene/GltfLoaderSpec.js b/Specs/Scene/GltfLoaderSpec.js index 6d873f559816..a0de7b109aee 100644 --- a/Specs/Scene/GltfLoaderSpec.js +++ b/Specs/Scene/GltfLoaderSpec.js @@ -151,6 +151,8 @@ describe( }).toThrowDeveloperError(); }); + // This is a no-op because GltfJsonLoader automatically upgrades the + // glTF version. This should work once that behavior is removed. it("throws if loading an unsupported glTF version", function () { function modifyGltf(gltf) { gltf.asset.version = "1.0"; @@ -177,20 +179,6 @@ describe( ); }); - it("throws if glb has invalid magic number", function () { - const gltfLoader = new GltfLoader({ - gltfResource: new Resource({ - url: boxTexturedBinary, - }), - incrementallyLoadTextures: false, - typedArray: new Uint8Array(20), - }); - - expect(function () { - gltfLoader.load(); - }).toThrowError(); - }); - function getOptions(gltfPath, options) { const resource = new Resource({ url: gltfPath, diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index a26c5adf128c..61aed069a414 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -87,6 +87,8 @@ describe( const vertexColorTestUrl = "./Data/Models/PBR/VertexColorTest/VertexColorTest.gltf"; const emissiveTextureUrl = "./Data/Models/PBR/BoxEmissive/BoxEmissive.gltf"; + const boomBoxUrl = + "./Data/Models/GltfLoader/BoomBox/glTF-pbrSpecularGlossiness/BoomBox.gltf"; const riggedFigureUrl = "./Data/Models/rigged-figure-test/rigged-figure-test.gltf"; const dracoCesiumManUrl = @@ -635,6 +637,21 @@ describe( }); }); + it("renders model with the KHR_materials_pbrSpecularGlossiness extension", function () { + const resource = Resource.createIfNeeded(boomBoxUrl); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental( + { + gltf: gltf, + basePath: boomBoxUrl, + }, + scene + ).then(function (model) { + verifyRender(model, true); + }); + }); + }); + it("renders model with morph targets", function () { const resource = Resource.createIfNeeded(morphPrimitivesTestUrl); return resource.fetchJson().then(function (gltf) { @@ -1400,25 +1417,23 @@ describe( it("boundingSphere accounts for axis correction", function () { const resource = Resource.createIfNeeded(riggedFigureUrl); - const loadPromise = resource.fetchArrayBuffer(); - return loadPromise.then(function (buffer) { - return loadAndZoomToModelExperimental( - { gltf: new Uint8Array(buffer) }, - scene - ).then(function (model) { - // The bounding sphere should transform from z-forward - // to x-forward. - const boundingSphere = model.boundingSphere; - expect(boundingSphere).toBeDefined(); - expect(boundingSphere.center).toEqualEpsilon( - new Cartesian3(0.0320296511054039, 0, 0.7249599695205688), - CesiumMath.EPSILON3 - ); - expect(boundingSphere.radius).toEqualEpsilon( - 0.9484635280120018, - CesiumMath.EPSILON3 - ); - }); + return resource.fetchJson().then(function (gltf) { + return loadAndZoomToModelExperimental({ gltf: gltf }, scene).then( + function (model) { + // The bounding sphere should transform from z-forward + // to x-forward. + const boundingSphere = model.boundingSphere; + expect(boundingSphere).toBeDefined(); + expect(boundingSphere.center).toEqualEpsilon( + new Cartesian3(0.0320296511054039, 0, 0.7249599695205688), + CesiumMath.EPSILON3 + ); + expect(boundingSphere.radius).toEqualEpsilon( + 0.9484635280120018, + CesiumMath.EPSILON3 + ); + } + ); }); }); }); @@ -2883,7 +2898,7 @@ describe( }); }); - it("changing imageBasedLighting parameters works", function () { + it("changing imageBasedLightingFactor works", function () { return loadAndZoomToModelExperimental( { gltf: boxTexturedGltfUrl, @@ -2909,10 +2924,161 @@ describe( expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual(result); }); + }); + }); - ibl.luminanceAtZenith = 0.0; + it("changing luminanceAtZenith works", function () { + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + imageBasedLighting: new ImageBasedLighting({ + luminanceAtZenith: 0.0, + }), + }, + scene + ).then(function (model) { + const renderOptions = { + scene: scene, + time: defaultDate, + }; + + let result; + verifyRender(model, true); expect(renderOptions).toRenderAndCall(function (rgba) { - expect(rgba).toEqual(result); + result = rgba; + }); + + const ibl = model.imageBasedLighting; + ibl.luminanceAtZenith = 0.2; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(result); + }); + }); + }); + + it("changing sphericalHarmonicCoefficients works", function () { + if (!scene.highDynamicRangeSupported) { + return; + } + const L00 = new Cartesian3( + 0.692622075009195, + 0.4543516001819, + 0.36910172299235 + ); // L00, irradiance, pre-scaled base + const L1_1 = new Cartesian3( + 0.289407068366422, + 0.16789310162658, + 0.106174907004792 + ); // L1-1, irradiance, pre-scaled base + const L10 = new Cartesian3( + -0.591502034778913, + -0.28152432317119, + 0.124647554708491 + ); // L10, irradiance, pre-scaled base + const L11 = new Cartesian3( + 0.34945458117126, + 0.163273486841657, + -0.03095643545207 + ); // L11, irradiance, pre-scaled base + const L2_2 = new Cartesian3( + 0.22171176447426, + 0.11771991868122, + 0.031381053430064 + ); // L2-2, irradiance, pre-scaled base + const L2_1 = new Cartesian3( + -0.348955284677868, + -0.187256994042823, + -0.026299717727617 + ); // L2-1, irradiance, pre-scaled base + const L20 = new Cartesian3( + 0.119982671127227, + 0.076784552175028, + 0.055517838847755 + ); // L20, irradiance, pre-scaled base + const L21 = new Cartesian3( + -0.545546043202299, + -0.279787444030397, + -0.086854000285261 + ); // L21, irradiance, pre-scaled base + const L22 = new Cartesian3( + 0.160417569726332, + 0.120896423762313, + 0.121102528320197 + ); // L22, irradiance, pre-scaled base + const coefficients = [L00, L1_1, L10, L11, L2_2, L2_1, L20, L21, L22]; + return loadAndZoomToModelExperimental( + { + gltf: boxTexturedGltfUrl, + imageBasedLighting: new ImageBasedLighting({ + sphericalHarmonicCoefficients: coefficients, + }), + }, + scene + ).then(function (model) { + scene.highDynamicRange = true; + + const renderOptions = { + scene: scene, + time: defaultDate, + }; + + let result; + verifyRender(model, true); + expect(renderOptions).toRenderAndCall(function (rgba) { + result = rgba; + }); + + const ibl = model.imageBasedLighting; + ibl.sphericalHarmonicCoefficients = undefined; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(result); + }); + + scene.highDynamicRange = false; + }); + }); + + it("changing specularEnvironmentMaps works", function () { + if (!scene.highDynamicRangeSupported) { + return; + } + const url = "./Data/EnvironmentMap/kiara_6_afternoon_2k_ibl.ktx2"; + return loadAndZoomToModelExperimental( + { + gltf: boomBoxUrl, + imageBasedLighting: new ImageBasedLighting({ + specularEnvironmentMaps: url, + }), + }, + scene + ).then(function (model) { + const ibl = model.imageBasedLighting; + + return pollToPromise(function () { + scene.render(); + return ( + defined(ibl.specularEnvironmentMapAtlas) && + ibl.specularEnvironmentMapAtlas.ready + ); + }).then(function () { + scene.highDynamicRange = true; + + const renderOptions = { + scene: scene, + time: defaultDate, + }; + + let result; + verifyRender(model, true); + expect(renderOptions).toRenderAndCall(function (rgba) { + result = rgba; + }); + + ibl.specularEnvironmentMaps = undefined; + expect(renderOptions).toRenderAndCall(function (rgba) { + expect(rgba).not.toEqual(result); + }); + scene.highDynamicRange = false; }); }); }); From 885bd65a66e489b97817ebcd6d1cc520a4aabc2c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 27 Jul 2022 17:42:12 -0400 Subject: [PATCH 09/10] Fix color bug --- Source/Scene/ModelExperimental/CPUStylingPipelineStage.js | 7 ++++--- Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl | 2 +- Specs/Scene/ModelExperimental/ModelExperimentalSpec.js | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Scene/ModelExperimental/CPUStylingPipelineStage.js b/Source/Scene/ModelExperimental/CPUStylingPipelineStage.js index d87011434c3f..c3d995931328 100644 --- a/Source/Scene/ModelExperimental/CPUStylingPipelineStage.js +++ b/Source/Scene/ModelExperimental/CPUStylingPipelineStage.js @@ -1,11 +1,12 @@ +import ColorBlendMode from "../ColorBlendMode.js"; import CPUStylingStageVS from "../../Shaders/ModelExperimental/CPUStylingStageVS.js"; import CPUStylingStageFS from "../../Shaders/ModelExperimental/CPUStylingStageFS.js"; +import defined from "../../Core/defined.js"; +import ModelColorPipelineStage from "./ModelColorPipelineStage.js"; import Pass from "../../Renderer/Pass.js"; -import ColorBlendMode from "../ColorBlendMode.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import StyleCommandsNeeded from "./StyleCommandsNeeded.js"; -import ModelColorPipelineStage from "./ModelColorPipelineStage.js"; -import defined from "../../Core/defined.js"; + /** * The CPU styling stage is responsible for ensuring that the feature's color is applied at runtime. * diff --git a/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl b/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl index 1f70ac3c4242..1948e44e95c8 100644 --- a/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl +++ b/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl @@ -66,7 +66,7 @@ void main() cpuStylingStage(material, selectedFeature); #endif - #ifdef HAS_MODEL_COLOR + #if defined(HAS_MODEL_COLOR) && !defined(USE_CPU_STYLING) modelColorStage(material); #endif diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index 61aed069a414..bc16a1237eb2 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -2838,6 +2838,10 @@ describe( }); describe("imageBasedLighting", function () { + afterEach(function () { + scene.highDynamicRange = false; + }); + it("initializes with imageBasedLighting", function () { const ibl = new ImageBasedLighting({ imageBasedLightingFactor: Cartesian2.ZERO, @@ -3033,8 +3037,6 @@ describe( expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual(result); }); - - scene.highDynamicRange = false; }); }); @@ -3078,7 +3080,6 @@ describe( expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual(result); }); - scene.highDynamicRange = false; }); }); }); From a2f6cfa526aa2557a915dd61ad9e3b493cb240b4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 28 Jul 2022 09:29:22 -0400 Subject: [PATCH 10/10] Restore color default to undefined --- Source/Scene/ModelExperimental/ModelExperimental.js | 9 +++++---- .../Shaders/ModelExperimental/ModelExperimentalFS.glsl | 2 +- Specs/Scene/ModelExperimental/ModelExperimentalSpec.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 5726d012e966..6007b7cd883f 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -66,7 +66,7 @@ import SplitDirection from "../SplitDirection.js"; * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. - * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. + * @param {Color} [options.color] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. @@ -195,8 +195,7 @@ export default function ModelExperimental(options) { this._id = options.id; this._idDirty = false; - const color = defaultValue(options.color, Color.WHITE); - this._color = Color.clone(color); + this._color = Color.clone(options.color); this._colorBlendMode = defaultValue( options.colorBlendMode, ColorBlendMode.HIGHLIGHT @@ -943,6 +942,8 @@ Object.defineProperties(ModelExperimental.prototype, { * @memberof ModelExperimental.prototype * * @type {Color} + * + * @default undefined */ color: { get: function () { @@ -2361,7 +2362,7 @@ ModelExperimental.prototype.destroyModelResources = function () { * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. - * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. + * @param {Color} [options.color] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. diff --git a/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl b/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl index 1948e44e95c8..1f70ac3c4242 100644 --- a/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl +++ b/Source/Shaders/ModelExperimental/ModelExperimentalFS.glsl @@ -66,7 +66,7 @@ void main() cpuStylingStage(material, selectedFeature); #endif - #if defined(HAS_MODEL_COLOR) && !defined(USE_CPU_STYLING) + #ifdef HAS_MODEL_COLOR modelColorStage(material); #endif diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js index bc16a1237eb2..edc8e29f18ec 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalSpec.js @@ -470,7 +470,7 @@ describe( expect(model.scene).toBeUndefined(); expect(model.distanceDisplayCondition).toBeUndefined(); - expect(model.color).toEqual(Color.WHITE); + expect(model.color).toBeUndefined(); expect(model.colorBlendMode).toEqual(ColorBlendMode.HIGHLIGHT); expect(model.colorBlendAmount).toEqual(0.5); expect(model.silhouetteColor).toEqual(Color.RED);