import { Cartesian3 } from "../../Source/Cesium.js"; import { Color } from "../../Source/Cesium.js"; import { HeadingPitchRange } from "../../Source/Cesium.js"; import { HeadingPitchRoll } from "../../Source/Cesium.js"; import { Transforms } from "../../Source/Cesium.js"; import { Cesium3DTilePass } from "../../Source/Cesium.js"; import { ClippingPlane } from "../../Source/Cesium.js"; import { ClippingPlaneCollection } from "../../Source/Cesium.js"; import { Model } from "../../Source/Cesium.js"; import Cesium3DTilesTester from "../Cesium3DTilesTester.js"; import createScene from "../createScene.js"; describe( "Scene/Instanced3DModel3DTileContent", function () { var scene; var centerLongitude = -1.31968; var centerLatitude = 0.698874; var gltfExternalUrl = "./Data/Cesium3DTiles/Instanced/InstancedGltfExternal/tileset.json"; var withBatchTableUrl = "./Data/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json"; var withBatchTableBinaryUrl = "./Data/Cesium3DTiles/Instanced/InstancedWithBatchTableBinary/tileset.json"; var withoutBatchTableUrl = "./Data/Cesium3DTiles/Instanced/InstancedWithoutBatchTable/tileset.json"; var orientationUrl = "./Data/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json"; var oct16POrientationUrl = "./Data/Cesium3DTiles/Instanced/InstancedOct32POrientation/tileset.json"; var scaleUrl = "./Data/Cesium3DTiles/Instanced/InstancedScale/tileset.json"; var scaleNonUniformUrl = "./Data/Cesium3DTiles/Instanced/InstancedScaleNonUniform/tileset.json"; var rtcUrl = "./Data/Cesium3DTiles/Instanced/InstancedRTC/tileset.json"; var quantizedUrl = "./Data/Cesium3DTiles/Instanced/InstancedQuantized/tileset.json"; var quantizedOct32POrientationUrl = "./Data/Cesium3DTiles/Instanced/InstancedQuantizedOct32POrientation/tileset.json"; var withTransformUrl = "./Data/Cesium3DTiles/Instanced/InstancedWithTransform/tileset.json"; var withBatchIdsUrl = "./Data/Cesium3DTiles/Instanced/InstancedWithBatchIds/tileset.json"; var texturedUrl = "./Data/Cesium3DTiles/Instanced/InstancedTextured/tileset.json"; function setCamera(longitude, latitude) { // One instance is located at the center, point the camera there var center = Cartesian3.fromRadians(longitude, latitude); scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 27.0)); } beforeAll(function () { scene = createScene(); }); beforeEach(function () { scene.morphTo3D(0.0); setCamera(centerLongitude, centerLatitude); }); afterAll(function () { scene.destroyForSpecs(); }); afterEach(function () { scene.primitives.removeAll(); }); it("throws with invalid format", function () { var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ gltfFormat: 2, }); Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "i3dm"); }); it("throws with invalid version", function () { var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ version: 2, }); Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "i3dm"); }); it("throws with empty gltf", function () { // Expect to throw DeveloperError in Model due to invalid gltf magic var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer(); Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "i3dm"); }); it("resolves readyPromise", function () { return Cesium3DTilesTester.resolvesReadyPromise( scene, withoutBatchTableUrl ); }); it("rejects readyPromise on error", function () { // Try loading a tile with an invalid url. // Expect promise to be rejected in Model, then in ModelInstanceCollection, and // finally in Instanced3DModel3DTileContent. var arrayBuffer = Cesium3DTilesTester.generateInstancedTileBuffer({ gltfFormat: 0, gltfUri: "not-a-real-path", }); return Cesium3DTilesTester.rejectsReadyPromiseOnError( scene, arrayBuffer, "i3dm" ); }); it("renders with external gltf", function () { return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with batch table", function () { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with batch table binary", function () { return Cesium3DTilesTester.loadTileset( scene, withBatchTableBinaryUrl ).then(function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it("renders without batch table", function () { return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with feature defined orientation", function () { return Cesium3DTilesTester.loadTileset(scene, orientationUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with feature defined Oct32P encoded orientation", function () { return Cesium3DTilesTester.loadTileset(scene, oct16POrientationUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with feature defined scale", function () { return Cesium3DTilesTester.loadTileset(scene, scaleUrl).then(function ( tileset ) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it("renders with feature defined non-uniform scale", function () { return Cesium3DTilesTester.loadTileset(scene, scaleNonUniformUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with RTC_CENTER semantic", function () { return Cesium3DTilesTester.loadTileset(scene, rtcUrl).then(function ( tileset ) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it("renders with feature defined quantized position", function () { return Cesium3DTilesTester.loadTileset(scene, quantizedUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with feature defined quantized position and Oct32P encoded orientation", function () { return Cesium3DTilesTester.loadTileset( scene, quantizedOct32POrientationUrl ).then(function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it("renders with batch ids", function () { return Cesium3DTilesTester.loadTileset(scene, withBatchIdsUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with tile transform", function () { return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); var newLongitude = -1.31962; var newLatitude = 0.698874; var newCenter = Cartesian3.fromRadians( newLongitude, newLatitude, 10.0 ); var newTransform = Transforms.headingPitchRollToFixedFrame( newCenter, new HeadingPitchRoll() ); // Update tile transform tileset.root.transform = newTransform; // Move the camera to the new location setCamera(newLongitude, newLatitude); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders with textures", function () { return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function ( tileset ) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it("renders in 2D", function () { return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); tileset.maximumScreenSpaceError = 2.0; scene.morphTo2D(0.0); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders in 2D with tile transform", function () { return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); tileset.maximumScreenSpaceError = 2.0; scene.morphTo2D(0.0); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders in CV", function () { return Cesium3DTilesTester.loadTileset(scene, gltfExternalUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); scene.morphToColumbusView(0.0); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders in CV with tile transform", function () { return Cesium3DTilesTester.loadTileset(scene, withTransformUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); scene.morphToColumbusView(0.0); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); }); it("renders when instancing is disabled", function () { // Disable extension var instancedArrays = scene.context._instancedArrays; scene.context._instancedArrays = undefined; return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then( function (tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); // Re-enable extension scene.context._instancedArrays = instancedArrays; } ); }); it("throws when calling getFeature with invalid index", function () { return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then( function (tileset) { var content = tileset.root.content; expect(function () { content.getFeature(-1); }).toThrowDeveloperError(); expect(function () { content.getFeature(10000); }).toThrowDeveloperError(); expect(function () { content.getFeature(); }).toThrowDeveloperError(); } ); }); it("gets memory usage", function () { return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function ( tileset ) { var content = tileset.root.content; // Box model - 36 ushort indices and 24 vertices per building, 8 float components (position, normal, uv) per vertex. // (24 * 8 * 4) + (36 * 2) = 840 var geometryByteLength = 840; // Texture is 128x128 RGBA bytes, not mipmapped var texturesByteLength = 65536; // One RGBA byte pixel per feature var batchTexturesByteLength = content.featuresLength * 4; var pickTexturesByteLength = content.featuresLength * 4; // Features have not been picked or colored yet, so the batch table contribution is 0. expect(content.geometryByteLength).toEqual(geometryByteLength); expect(content.texturesByteLength).toEqual(texturesByteLength); expect(content.batchTableByteLength).toEqual(0); // Color a feature and expect the texture memory to increase content.getFeature(0).color = Color.RED; scene.renderForSpecs(); expect(content.geometryByteLength).toEqual(geometryByteLength); expect(content.texturesByteLength).toEqual(texturesByteLength); expect(content.batchTableByteLength).toEqual(batchTexturesByteLength); // Pick the tile and expect the texture memory to increase scene.pickForSpecs(); expect(content.geometryByteLength).toEqual(geometryByteLength); expect(content.texturesByteLength).toEqual(texturesByteLength); expect(content.batchTableByteLength).toEqual( batchTexturesByteLength + pickTexturesByteLength ); }); }); it("Links model to tileset clipping planes based on bounding volume clipping", function () { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( function (tileset) { var tile = tileset.root; var content = tile.content; var model = content._modelInstanceCollection._model; var passOptions = Cesium3DTilePass.getPassOptions( Cesium3DTilePass.RENDER ); expect(model.clippingPlanes).toBeUndefined(); var clippingPlaneCollection = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], }); tileset.clippingPlanes = clippingPlaneCollection; clippingPlaneCollection.update(scene.frameState); tile.update(tileset, scene.frameState, passOptions); expect(model.clippingPlanes).toBeDefined(); expect(model.clippingPlanes).toBe(tileset.clippingPlanes); tile._isClipped = false; tile.update(tileset, scene.frameState, passOptions); expect(model.clippingPlanes).toBeUndefined(); } ); }); it("Links model to tileset clipping planes if tileset clipping planes are reassigned", function () { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( function (tileset) { var tile = tileset.root; var model = tile.content._modelInstanceCollection._model; var passOptions = Cesium3DTilePass.getPassOptions( Cesium3DTilePass.RENDER ); expect(model.clippingPlanes).toBeUndefined(); var clippingPlaneCollection = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], }); tileset.clippingPlanes = clippingPlaneCollection; clippingPlaneCollection.update(scene.frameState); tile.update(tileset, scene.frameState, passOptions); expect(model.clippingPlanes).toBeDefined(); expect(model.clippingPlanes).toBe(tileset.clippingPlanes); var newClippingPlaneCollection = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], }); tileset.clippingPlanes = newClippingPlaneCollection; newClippingPlaneCollection.update(scene.frameState); expect(model.clippingPlanes).not.toBe(tileset.clippingPlanes); tile.update(tileset, scene.frameState, passOptions); expect(model.clippingPlanes).toBe(tileset.clippingPlanes); } ); }); it("rebuilds Model shaders when clipping planes change", function () { spyOn(Model, "_getClippingFunction").and.callThrough(); return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( function (tileset) { var tile = tileset.root; var content = tile.content; var clippingPlaneCollection = new ClippingPlaneCollection({ planes: [new ClippingPlane(Cartesian3.UNIT_X, 0.0)], }); var passOptions = Cesium3DTilePass.getPassOptions( Cesium3DTilePass.RENDER ); tileset.clippingPlanes = clippingPlaneCollection; clippingPlaneCollection.update(scene.frameState); content.clippingPlanesDirty = true; tile.update(tileset, scene.frameState, passOptions); expect(Model._getClippingFunction.calls.count()).toEqual(1); } ); }); it("destroys", function () { return Cesium3DTilesTester.tileDestroys(scene, withoutBatchTableUrl); }); }, "WebGL" );