import { Axis, Cesium3DTileStyle, Color, CustomShader, CustomShaderPipelineStage, Matrix4, ModelColorPipelineStage, ModelExperimentalSceneGraph, Pass, ResourceCache, } from "../../../Source/Cesium.js"; import createScene from "../../createScene.js"; import loadAndZoomToModelExperimental from "./loadAndZoomToModelExperimental.js"; describe( "Scene/ModelExperimental/ModelExperimentalSceneGraph", function () { var parentGltfUrl = "./Data/Cesium3DTiles/GltfContent/glTF/parent.gltf"; var vertexColorGltfUrl = "./Data/Models/PBR/VertexColorTest/VertexColorTest.gltf"; var buildingsMetadata = "./Data/Models/GltfLoader/BuildingsMetadata/glTF/buildings-metadata.gltf"; var scene; beforeAll(function () { scene = createScene(); }); afterAll(function () { scene.destroyForSpecs(); }); afterEach(function () { scene.primitives.removeAll(); ResourceCache.clearForSpecs(); }); it("creates runtime nodes and runtime primitives from a model", function () { return loadAndZoomToModelExperimental( { gltf: vertexColorGltfUrl }, scene ).then(function (model) { var sceneGraph = model._sceneGraph; var modelComponents = sceneGraph._modelComponents; expect(sceneGraph).toBeDefined(); var runtimeNodes = sceneGraph._runtimeNodes; expect(runtimeNodes.length).toEqual(modelComponents.nodes.length); expect(runtimeNodes[0].runtimePrimitives.length).toEqual(1); expect(runtimeNodes[1].runtimePrimitives.length).toEqual(1); }); }); it("builds draw commands for all opaque styled features", function () { return loadAndZoomToModelExperimental( { gltf: buildingsMetadata, }, scene ).then(function (model) { model.style = new Cesium3DTileStyle({ color: { conditions: [["${height} > 1", "color('red')"]], }, }); var frameState = scene.frameState; var sceneGraph = model._sceneGraph; // Reset the draw commands so we can inspect the draw command generation. model._drawCommandsBuilt = false; sceneGraph._drawCommands = []; frameState.commandList = []; scene.renderForSpecs(); expect(sceneGraph._drawCommands.length).toEqual(1); expect(frameState.commandList.length).toEqual(1); expect(sceneGraph._drawCommands[0].pass).toEqual(Pass.OPAQUE); }); }); it("builds draw commands for all translucent styled features", function () { return loadAndZoomToModelExperimental( { gltf: buildingsMetadata, }, scene ).then(function (model) { model.style = new Cesium3DTileStyle({ color: { conditions: [["${height} > 1", "color('red', 0.1)"]], }, }); var frameState = scene.frameState; var sceneGraph = model._sceneGraph; // Reset the draw commands so we can inspect the draw command generation. model._drawCommandsBuilt = false; sceneGraph._drawCommands = []; frameState.commandList = []; scene.renderForSpecs(); expect(sceneGraph._drawCommands.length).toEqual(1); expect(frameState.commandList.length).toEqual(1); expect(sceneGraph._drawCommands[0].pass).toEqual(Pass.TRANSLUCENT); }); }); it("builds draw commands for both opaque and translucent styled features", function () { return loadAndZoomToModelExperimental( { gltf: buildingsMetadata, }, scene ).then(function (model) { model.style = new Cesium3DTileStyle({ color: { conditions: [ ["${height} > 80", "color('red', 0.1)"], ["true", "color('blue')"], ], }, }); var frameState = scene.frameState; var sceneGraph = model._sceneGraph; // Reset the draw commands so we can inspect the draw command generation. model._drawCommandsBuilt = false; sceneGraph._drawCommands = []; frameState.commandList = []; scene.renderForSpecs(); expect(sceneGraph._drawCommands.length).toEqual(2); expect(frameState.commandList.length).toEqual(2); expect(sceneGraph._drawCommands[0].pass).toEqual(Pass.OPAQUE); expect(sceneGraph._drawCommands[1].pass).toEqual(Pass.TRANSLUCENT); }); }); it("builds draw commands for each primitive", function () { spyOn( ModelExperimentalSceneGraph.prototype, "buildDrawCommands" ).and.callThrough(); return loadAndZoomToModelExperimental( { gltf: parentGltfUrl }, scene ).then(function (model) { var sceneGraph = model._sceneGraph; var runtimeNodes = sceneGraph._runtimeNodes; var primitivesCount = 0; for (var i = 0; i < runtimeNodes.length; i++) { primitivesCount += runtimeNodes[i].runtimePrimitives.length; } var frameState = scene.frameState; frameState.commandList = []; scene.renderForSpecs(); expect( ModelExperimentalSceneGraph.prototype.buildDrawCommands ).toHaveBeenCalled(); expect(frameState.commandList.length).toEqual(primitivesCount); expect(model._drawCommandsBuilt).toEqual(true); expect(sceneGraph._drawCommands.length).toEqual(primitivesCount); // Reset the draw command list to see if they're re-built. model._drawCommandsBuilt = false; sceneGraph._drawCommands = []; frameState.commandList = []; scene.renderForSpecs(); expect( ModelExperimentalSceneGraph.prototype.buildDrawCommands ).toHaveBeenCalled(); expect(frameState.commandList.length).toEqual(primitivesCount); }); }); it("traverses scene graph correctly", function () { return loadAndZoomToModelExperimental( { gltf: parentGltfUrl }, scene ).then(function (model) { var sceneGraph = model._sceneGraph; var modelComponents = sceneGraph._modelComponents; var runtimeNodes = sceneGraph._runtimeNodes; expect(runtimeNodes[1].node).toEqual(modelComponents.nodes[0]); expect(runtimeNodes[0].node).toEqual(modelComponents.nodes[1]); }); }); it("propagates node transforms correctly", function () { return loadAndZoomToModelExperimental( { gltf: parentGltfUrl, upAxis: Axis.Z, forwardAxis: Axis.X, }, scene ).then(function (model) { var sceneGraph = model._sceneGraph; var modelComponents = sceneGraph._modelComponents; var scene = modelComponents.scene; var runtimeNodes = sceneGraph._runtimeNodes; expect(scene.upAxis).toEqual(Axis.Z); expect(scene.forwardAxis).toEqual(Axis.X); expect(runtimeNodes[1].modelMatrix).toEqual( modelComponents.nodes[0].matrix ); expect(runtimeNodes[0].modelMatrix).toEqual( Matrix4.multiplyByTranslation( runtimeNodes[1].modelMatrix, modelComponents.nodes[1].translation, new Matrix4() ) ); }); }); it("adds ModelColorPipelineStage when color is set on the model", function () { spyOn(ModelColorPipelineStage, "process"); return loadAndZoomToModelExperimental( { color: Color.RED, gltf: parentGltfUrl, }, scene ).then(function () { expect(ModelColorPipelineStage.process).toHaveBeenCalled(); }); }); it("adds CustomShaderPipelineStage when customShader is set on the model", function () { spyOn(CustomShaderPipelineStage, "process"); return loadAndZoomToModelExperimental( { gltf: buildingsMetadata, }, scene ).then(function (model) { model.customShader = new CustomShader(); model.update(scene.frameState); expect(CustomShaderPipelineStage.process).toHaveBeenCalled(); }); }); it("throws for undefined options.model", function () { expect(function () { return new ModelExperimentalSceneGraph({ model: undefined, modelComponents: {}, }); }).toThrowDeveloperError(); }); it("throws for undefined options.modelComponents", function () { expect(function () { return new ModelExperimentalSceneGraph({ model: {}, modelComponents: undefined, }); }).toThrowDeveloperError(); }); }, "WebGL" );