defineSuite([ 'Scene/Batched3DModel3DTileContent', 'Core/Cartesian3', 'Core/Color', 'Core/HeadingPitchRange', 'Core/HeadingPitchRoll', 'Core/Matrix4', 'Core/Transforms', 'Scene/ClippingPlane', 'Scene/ClippingPlaneCollection', 'Scene/Model', 'Specs/Cesium3DTilesTester', 'Specs/createScene' ], function( Batched3DModel3DTileContent, Cartesian3, Color, HeadingPitchRange, HeadingPitchRoll, Matrix4, Transforms, ClippingPlane, ClippingPlaneCollection, Model, Cesium3DTilesTester, createScene) { 'use strict'; var scene; var centerLongitude = -1.31968; var centerLatitude = 0.698874; var withBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json'; var withBatchTableBinaryUrl = './Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/tileset.json'; var withoutBatchTableUrl = './Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/tileset.json'; var translucentUrl = './Data/Cesium3DTiles/Batched/BatchedTranslucent/tileset.json'; var translucentOpaqueMixUrl = './Data/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/tileset.json'; var withTransformBoxUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json'; var withTransformSphereUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformSphere/tileset.json'; var withTransformRegionUrl = './Data/Cesium3DTiles/Batched/BatchedWithTransformRegion/tileset.json'; var texturedUrl = './Data/Cesium3DTiles/Batched/BatchedTextured/tileset.json'; var deprecated1Url = './Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json'; var deprecated2Url = './Data/Cesium3DTiles/Batched/BatchedDeprecated2/tileset.json'; var withRtcCenterUrl = './Data/Cesium3DTiles/Batched/BatchedWithRtcCenter/tileset.json'; function setCamera(longitude, latitude) { // One feature 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, 15.0)); } beforeAll(function() { scene = createScene(); // Keep the error from logging to the console when running tests spyOn(Batched3DModel3DTileContent, '_deprecationWarning'); }); afterAll(function() { scene.destroyForSpecs(); }); beforeEach(function() { setCamera(centerLongitude, centerLatitude); }); afterEach(function() { scene.primitives.removeAll(); }); it('throws with invalid version', function() { var arrayBuffer = Cesium3DTilesTester.generateBatchedTileBuffer({ version : 2 }); Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'b3dm'); }); it('recognizes the legacy 20-byte header', function() { return Cesium3DTilesTester.loadTileset(scene, deprecated1Url) .then(function(tileset) { expect(Batched3DModel3DTileContent._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); var batchTable = tileset.root.content.batchTable; expect(batchTable._properties).toBeDefined(); }); }); it('recognizes the legacy 24-byte header', function() { return Cesium3DTilesTester.loadTileset(scene, deprecated2Url) .then(function(tileset) { expect(Batched3DModel3DTileContent._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); var batchTable = tileset.root.content.batchTable; expect(batchTable._properties).toBeDefined(); }); }); it('logs deprecation warning for use of BATCHID without prefixed underscore', function() { return Cesium3DTilesTester.loadTileset(scene, deprecated1Url) .then(function(tileset) { expect(Batched3DModel3DTileContent._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it('throws with empty gltf', function() { // Expect to throw DeveloperError in Model due to invalid gltf magic var arrayBuffer = Cesium3DTilesTester.generateBatchedTileBuffer(); Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, 'b3dm'); }); it('resolves readyPromise', function() { return Cesium3DTilesTester.resolvesReadyPromise(scene, withoutBatchTableUrl); }); 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 all features translucent', function() { return Cesium3DTilesTester.loadTileset(scene, translucentUrl).then(function(tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it('renders with a mix of opaque and translucent features', function() { return Cesium3DTilesTester.loadTileset(scene, translucentOpaqueMixUrl).then(function(tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); }); it('renders with textures', function() { return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function(tileset) { Cesium3DTilesTester.expectRender(scene, tileset); }); }); function expectRenderWithTransform(url) { return Cesium3DTilesTester.loadTileset(scene, url).then(function(tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); var newLongitude = -1.31962; var newLatitude = 0.698874; var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude, 0.0); var newHPR = new HeadingPitchRoll(); var newTransform = Transforms.headingPitchRollToFixedFrame(newCenter, newHPR); // Update tile transform tileset.root.transform = newTransform; scene.renderForSpecs(); // Move the camera to the new location setCamera(newLongitude, newLatitude); Cesium3DTilesTester.expectRenderTileset(scene, tileset); }); } it('renders with a tile transform and box bounding volume', function() { return expectRenderWithTransform(withTransformBoxUrl); }); it('renders with a tile transform and sphere bounding volume', function() { return expectRenderWithTransform(withTransformSphereUrl); }); it('renders with a tile transform and region bounding volume', function() { return expectRenderWithTransform(withTransformRegionUrl); }); it('picks with batch table', function() { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { var content = tileset.root.content; tileset.show = false; expect(scene).toPickPrimitive(undefined); tileset.show = true; expect(scene).toPickAndCall(function(result) { expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); }); }); }); it('picks without batch table', function() { return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then(function(tileset) { var content = tileset.root.content; tileset.show = false; expect(scene).toPickPrimitive(undefined); tileset.show = true; expect(scene).toPickAndCall(function(result) { expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); }); }); }); it('can get features and properties', function() { return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { var content = tileset.root.content; expect(content.featuresLength).toBe(10); expect(content.innerContents).toBeUndefined(); expect(content.hasProperty(0, 'id')).toBe(true); expect(content.getFeature(0)).toBeDefined(); }); }); 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(1000); }).toThrowDeveloperError(); expect(function(){ content.getFeature(); }).toThrowDeveloperError(); }); }); it('gets memory usage', function() { return Cesium3DTilesTester.loadTileset(scene, texturedUrl).then(function(tileset) { var content = tileset.root.content; // 10 buildings, 36 ushort indices and 24 vertices per building, 8 float components (position, normal, uv) and 1 uint component (batchId) per vertex. // 10 * ((24 * (8 * 4 + 1 * 4)) + (36 * 2)) = 9360 var geometryByteLength = 9360; // 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._model; 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); expect(model.clippingPlanes).toBeDefined(); expect(model.clippingPlanes).toBe(tileset.clippingPlanes); tile._isClipped = false; tile.update(tileset, scene.frameState); 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._model; 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); 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); 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 clippingPlaneCollection = new ClippingPlaneCollection({ planes : [ new ClippingPlane(Cartesian3.UNIT_X, 0.0) ] }); tileset.clippingPlanes = clippingPlaneCollection; clippingPlaneCollection.update(scene.frameState); tile.update(tileset, scene.frameState); expect(Model._getClippingFunction.calls.count()).toEqual(1); }); }); it('transforms model positions by RTC_CENTER property in the features table', function() { return Cesium3DTilesTester.loadTileset(scene, withRtcCenterUrl).then(function(tileset) { Cesium3DTilesTester.expectRenderTileset(scene, tileset); var rtcTransform = tileset.root.content._rtcCenterTransform; expect(rtcTransform).toEqual(Matrix4.fromTranslation(new Cartesian3(0.1, 0.2, 0.3))); var expectedModelTransform = Matrix4.multiply(tileset.root.transform, rtcTransform, new Matrix4()); expect(tileset.root.content._contentModelMatrix).toEqual(expectedModelTransform); expect(tileset.root.content._model._modelMatrix).toEqual(expectedModelTransform); // Update tile transform var newLongitude = -1.31962; var newLatitude = 0.698874; var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude, 0.0); var newHPR = new HeadingPitchRoll(); var newTransform = Transforms.headingPitchRollToFixedFrame(newCenter, newHPR); tileset.root.transform = newTransform; scene.camera.lookAt(newCenter, new HeadingPitchRange(0.0, 0.0, 15.0)); scene.renderForSpecs(); expectedModelTransform = Matrix4.multiply(tileset.root.computedTransform, rtcTransform, expectedModelTransform); expect(tileset.root.content._model._modelMatrix).toEqual(expectedModelTransform); }); }); it('destroys', function() { return Cesium3DTilesTester.tileDestroys(scene, withoutBatchTableUrl); }); }, 'WebGL');