From a88c1784d1cb0a3fe66b8d9f6721194e903f5ae5 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Mon, 11 Oct 2021 17:51:33 -0400 Subject: [PATCH 01/31] Adds ModelExperimental3DTileContent --- Source/Scene/Cesium3DTileContentFactory.js | 19 +- .../ModelExperimental3DTileContent.js | 363 ++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js diff --git a/Source/Scene/Cesium3DTileContentFactory.js b/Source/Scene/Cesium3DTileContentFactory.js index e8761d2a365f..3ead3b397692 100644 --- a/Source/Scene/Cesium3DTileContentFactory.js +++ b/Source/Scene/Cesium3DTileContentFactory.js @@ -8,6 +8,8 @@ import PointCloud3DTileContent from "./PointCloud3DTileContent.js"; import Tileset3DTileContent from "./Tileset3DTileContent.js"; import Vector3DTileContent from "./Vector3DTileContent.js"; import RuntimeError from "../Core/RuntimeError.js"; +import ExperimentalFeatures from "../Core/ExperimentalFeatures.js"; +import ModelExperimental3DTileContent from "./ModelExperimental/ModelExperimental3DTileContent.js"; /** * Maps a tile's magic field in its header to a new content object for the tile's payload. @@ -91,10 +93,25 @@ var Cesium3DTileContentFactory = { var dataView = new DataView(arrayBuffer, byteOffset); var byteLength = dataView.getUint32(8, true); var glb = new Uint8Array(arrayBuffer, byteOffset, byteLength); - + if (ExperimentalFeatures.enableModelExperimental) { + return ModelExperimental3DTileContent.fromGltf( + tileset, + tile, + resource, + glb + ); + } return new Gltf3DTileContent(tileset, tile, resource, glb); }, gltf: function (tileset, tile, resource, json) { + if (ExperimentalFeatures.enableModelExperimental) { + return ModelExperimental3DTileContent.fromGltf( + tileset, + tile, + resource, + json + ); + } return new Gltf3DTileContent(tileset, tile, resource, json); }, }; diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js new file mode 100644 index 000000000000..d8912464553d --- /dev/null +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -0,0 +1,363 @@ +import Axis from "../Axis.js"; +import Cesium3DTileFeatureTable from "../Cesium3DTileFeatureTable.js"; +import Color from "../../Core/Color.js"; +import defaultValue from "../../Core/defaultValue.js"; +import defined from "../../Core/defined.js"; +import deprecationWarning from "../../Core/deprecationWarning.js"; +import destroyObject from "../../Core/destroyObject.js"; +import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; +import ModelExperimental from "./ModelExperimental.js"; +import parseBatchTable from "../parseBatchTable.js"; +import Pass from "../../Renderer/Pass.js"; +import RuntimeError from "../../Core/RuntimeError.js"; + +export default function ModelExperimental3DTileContent( + tileset, + tile, + resource, + gltf, + featureMetadata +) { + this._tileset = tileset; + this._tile = tile; + this._resource = resource; + + initialize(this, gltf, featureMetadata); +} + +Object.defineProperties(ModelExperimental3DTileContent.prototype, { + featuresLength: { + get: function () { + return 0; + }, + }, + + pointsLength: { + get: function () { + return this._model.pointsLength; + }, + }, + + trianglesLength: { + get: function () { + return this._model.trianglesLength; + }, + }, + + geometryByteLength: { + get: function () { + return this._model.geometryByteLength; + }, + }, + + texturesByteLength: { + get: function () { + return this._model.texturesByteLength; + }, + }, + + batchTableByteLength: { + get: function () { + return 0; + }, + }, + + innerContents: { + get: function () { + return undefined; + }, + }, + + readyPromise: { + get: function () { + return this._model.readyPromise; + }, + }, + + tileset: { + get: function () { + return this._tileset; + }, + }, + + tile: { + get: function () { + return this._tile; + }, + }, + + url: { + get: function () { + return this._resource.getUrlComponent(true); + }, + }, + + batchTable: { + get: function () { + return this._featureTable; + }, + }, + + groupMetadata: { + get: function () { + return this._groupMetadata; + }, + set: function (value) { + this._groupMetadata = value; + }, + }, +}); + +function initialize(content, gltf) { + var tileset = content._tileset; + var tile = content._tile; + var resource = content._resource; + + var modelOptions = { + gltf: gltf, + cull: false, // The model is already culled by 3D Tiles + releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory + opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass + basePath: resource, + modelMatrix: tile.computedTransform, + upAxis: tileset._gltfUpAxis, + forwardAxis: Axis.X, + incrementallyLoadTextures: false, + customShader: tileset.customShader, + content: content, + }; + content._model = ModelExperimental.fromGltf(modelOptions); +} + +ModelExperimental3DTileContent.prototype.getFeature = function (featureId) { + if (!defined(this._featureTable)) { + return undefined; + } + return this._featureTable.getFeature(featureId); +}; + +ModelExperimental3DTileContent.prototype.hasProperty = function ( + featureId, + name +) { + if (!defined(this._featureTable)) { + return false; + } + return this._featureTable.hasProperty(featureId, name); +}; + +ModelExperimental3DTileContent.prototype.applyDebugSettings = function ( + enabled, + color +) { + color = enabled ? color : Color.WHITE; + this._model.color = color; +}; + +ModelExperimental3DTileContent.prototype.update = function ( + tileset, + frameState +) { + var model = this._model; + var tile = this._tile; + + model.modelMatrix = tile.computedTransform; + model.backFaceCulling = tileset.backFaceCulling; + + model.update(frameState); +}; + +ModelExperimental3DTileContent.prototype.isDestroyed = function () { + return false; +}; + +ModelExperimental3DTileContent.prototype.destroy = function () { + this._model = this._model && this._model.destroy(); + return destroyObject(this); +}; + +ModelExperimental3DTileContent.fromGltf = function ( + tileset, + tile, + resource, + gltf +) { + return new ModelExperimental3DTileContent(tileset, tile, resource, gltf); +}; + +var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; + +ModelExperimental3DTileContent.fromB3dm = function ( + tileset, + tile, + resource, + arrayBuffer, + byteOffset +) { + var byteStart = defaultValue(byteOffset, 0); + byteOffset = byteStart; + var dataView = new DataView(arrayBuffer); + // Skip B3DM magic. + byteOffset += UINT32_BYTE_SIZE; + var version = dataView.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError( + "Only Batched 3D Model version 1 is supported. Version " + + version + + " is not." + ); + } + byteOffset += UINT32_BYTE_SIZE; + + var byteLength = dataView.getUint32(byteOffset, true); + byteOffset += UINT32_BYTE_SIZE; + var featureTableJsonByteLength = dataView.getUint32(byteOffset, true); + byteOffset += UINT32_BYTE_SIZE; + var featureTableBinaryByteLength = dataView.getUint32(byteOffset, true); + byteOffset += UINT32_BYTE_SIZE; + var batchTableJsonByteLength = dataView.getUint32(byteOffset, true); + byteOffset += UINT32_BYTE_SIZE; + var batchTableBinaryByteLength = dataView.getUint32(byteOffset, true); + byteOffset += UINT32_BYTE_SIZE; + + var batchLength; + // Legacy header #1: [batchLength] [batchTableByteLength] + // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] + // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] + // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. + // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. + // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead + if (batchTableJsonByteLength >= 570425344) { + // First legacy check + byteOffset -= UINT32_BYTE_SIZE * 2; + batchLength = featureTableJsonByteLength; + batchTableJsonByteLength = featureTableBinaryByteLength; + batchTableBinaryByteLength = 0; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + ModelExperimental3DTileContent._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } else if (batchTableBinaryByteLength >= 570425344) { + // Second legacy check + byteOffset -= UINT32_BYTE_SIZE; + batchLength = batchTableJsonByteLength; + batchTableJsonByteLength = featureTableJsonByteLength; + batchTableBinaryByteLength = featureTableBinaryByteLength; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + ModelExperimental3DTileContent._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } + + var typedArray = new Uint8Array(arrayBuffer); + + var featureTableJson; + if (featureTableJsonByteLength === 0) { + featureTableJson = { + BATCH_LENGTH: defaultValue(batchLength, 0), + }; + } else { + featureTableJson = getJsonFromTypedArray( + typedArray, + byteOffset, + featureTableJsonByteLength + ); + byteOffset += featureTableJsonByteLength; + } + + var featureTableBinary = new Uint8Array( + arrayBuffer, + byteOffset, + featureTableBinaryByteLength + ); + byteOffset += featureTableBinaryByteLength; + + var featureTable = new Cesium3DTileFeatureTable( + featureTableJson, + featureTableBinary + ); + + batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); + featureTable.featuresLength = batchLength; + + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the + // arraybuffer/string compressed in memory and then decompress it when it is first accessed. + // + // We could also make another request for it, but that would make the property set/get + // API async, and would double the number of numbers in some cases. + batchTableJson = getJsonFromTypedArray( + typedArray, + byteOffset, + batchTableJsonByteLength + ); + byteOffset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array( + arrayBuffer, + byteOffset, + batchTableBinaryByteLength + ); + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + byteOffset += batchTableBinaryByteLength; + } + } + + var featureMetadata; + if (batchLength > 0 && defined(batchTableJson)) { + featureMetadata = parseBatchTable({ + count: batchLength, + batchTable: batchTableJson, + binaryBody: batchTableBinary, + }); + } + + var gltfByteLength = byteStart + byteLength - byteOffset; + if (gltfByteLength === 0) { + throw new RuntimeError("glTF byte length must be greater than 0."); + } + + var gltfTypedArray; + if (byteOffset % 4 === 0) { + gltfTypedArray = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); + } else { + // Create a copy of the glb so that it is 4-byte aligned + ModelExperimental3DTileContent._deprecationWarning( + "b3dm-glb-unaligned", + "The embedded glb is not aligned to a 4-byte boundary." + ); + gltfTypedArray = new Uint8Array( + typedArray.subarray(byteOffset, byteOffset + gltfByteLength) + ); + } + + content._rtcCenterTransform = Matrix4.IDENTITY; + var rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3 + ); + if (defined(rtcCenter)) { + content._rtcCenterTransform = Matrix4.fromTranslation( + Cartesian3.fromArray(rtcCenter) + ); + } + + content._contentModelMatrix = Matrix4.multiply( + tile.computedTransform, + content._rtcCenterTransform, + new Matrix4() + ); +}; + +// This can be overridden for testing purposes +ModelExperimental3DTileContent._deprecationWarning = deprecationWarning; From 94ff88ddc165741e909a2ca3e27c9fda5b661e40 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 13 Oct 2021 14:09:25 -0400 Subject: [PATCH 02/31] Removes dependency on content's feature table --- Source/Scene/Cesium3DTileContentFactory.js | 9 + Source/Scene/ModelExperimental/B3dmLoader.js | 513 ++++++++++++++++++ .../BatchTexturePipelineStage.js | 9 +- .../FeatureIdPipelineStage.js | 5 +- .../ModelExperimental/ModelExperimental.js | 85 +-- .../ModelExperimental3DTileContent.js | 227 ++------ .../ModelExperimentalPrimitive.js | 15 +- .../ModelExperimental/ModelFeatureTable.js | 24 +- .../ModelExperimental/PickingPipelineStage.js | 13 +- .../ModelExperimental/buildDrawCommand.js | 2 +- 10 files changed, 637 insertions(+), 265 deletions(-) create mode 100644 Source/Scene/ModelExperimental/B3dmLoader.js diff --git a/Source/Scene/Cesium3DTileContentFactory.js b/Source/Scene/Cesium3DTileContentFactory.js index 3ead3b397692..9f37dae5f466 100644 --- a/Source/Scene/Cesium3DTileContentFactory.js +++ b/Source/Scene/Cesium3DTileContentFactory.js @@ -18,6 +18,15 @@ import ModelExperimental3DTileContent from "./ModelExperimental/ModelExperimenta */ var Cesium3DTileContentFactory = { b3dm: function (tileset, tile, resource, arrayBuffer, byteOffset) { + if (ExperimentalFeatures.enableModelExperimental) { + return ModelExperimental3DTileContent.fromB3dm( + tileset, + tile, + resource, + arrayBuffer, + byteOffset + ); + } return new Batched3DModel3DTileContent( tileset, tile, diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js new file mode 100644 index 000000000000..86e7e441f956 --- /dev/null +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -0,0 +1,513 @@ +import Axis from "../Axis.js"; +import Cartesian3 from "../../Core/Cartesian3.js"; +import Cesium3DTileFeatureTable from "../Cesium3DTileFeatureTable.js"; +import Check from "../../Core/Check.js"; +import ComponentDatatype from "../../Core/ComponentDatatype.js"; +import defaultValue from "../../Core/defaultValue.js"; +import defined from "../../Core/defined.js"; +import deprecationWarning from "../../Core/deprecationWarning.js"; +import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; +import GltfLoader from "../GltfLoader.js"; +import parseBatchTable from "../parseBatchTable.js"; +import ResourceLoader from "../ResourceLoader.js"; +import RuntimeError from "../../Core/RuntimeError.js"; +import when from "../../ThirdParty/when.js"; +import Matrix4 from "../../Core/Matrix4.js"; + +var B3dmLoaderState = { + UNLOADED: 0, + LOADING: 1, + LOADED: 2, + PROCESSING: 3, + READY: 4, + FAILED: 5, +}; + +var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; + +/** + * Loads a Batched 3D Model. + *

+ * Implements the {@link ResourceLoader} interface. + *

+ * + * @alias B3dmLoader + * @constructor + * @augments ResourceLoader + * + * @param {Object} options Object with the following properties: + * @param {Resource} options.b3dmResource The {@link Resource} containing the B3DM. + * @param {ArrayBuffer} options.arrayBuffer The array buffer containing the B3DM contents. + * @param {Number} [options.byteOffset] The byte offset to the beginning of the B3DM contents in the typed array. + * @param {Resource} [options.baseResource] The {@link Resource} that paths in the glTF JSON are relative to. + * @param {Boolean} [options.releaseGltfJson=false] When true, the glTF JSON is released once the glTF is loaded. This is is especially useful for cases like 3D Tiles, where each .gltf model is unique and caching the glTF JSON is not effective. + * @param {Boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. + * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the glTF is loaded. + * @param {Axis} [options.upAxis=Axis.Y] The up-axis of the glTF model. + * @param {Axis} [options.forwardAxis=Axis.X] The forward-axis of the glTF model. + */ +function B3dmLoader(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var b3dmResource = options.b3dmResource; + var baseResource = options.baseResource; + var arrayBuffer = options.arrayBuffer; + var byteOffset = defaultValue(options.byteOffset, 0); + var releaseGltfJson = defaultValue(options.releaseGltfJson, false); + var asynchronous = defaultValue(options.asynchronous, true); + var incrementallyLoadTextures = defaultValue( + options.incrementallyLoadTextures, + true + ); + var upAxis = defaultValue(options.upAxis, Axis.Y); + var forwardAxis = defaultValue(options.forwardAxis, Axis.X); + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.b3dmResource", b3dmResource); + Check.typeOf.object("options.arrayBuffer", arrayBuffer); + //>>includeEnd('debug'); + + baseResource = defined(baseResource) ? baseResource : b3dmResource.clone(); + + this._b3dmResource = b3dmResource; + this._baseResource = baseResource; + this._arrayBuffer = arrayBuffer; + this._byteOffset = byteOffset; + this._releaseGltfJson = releaseGltfJson; + this._asynchronous = asynchronous; + this._incrementallyLoadTextures = incrementallyLoadTextures; + this._upAxis = upAxis; + this._forwardAxis = forwardAxis; + + // Since the 3D Tiles code handles loading of the tile contents, the B3dmLoader starts at the LOADED stage. + this._state = B3dmLoaderState.LOADED; + this._promise = when.defer(); + this._texturesLoadedPromise = when.defer(); + this._gltfLoader = undefined; + + // Loaded results. + this._batchLength = 0; + this._rtcTransform = undefined; + this._featureTable = undefined; + this._batchTable = undefined; + this._components = undefined; +} + +if (defined(Object.create)) { + B3dmLoader.prototype = Object.create(ResourceLoader.prototype); + B3dmLoader.prototype.constructor = B3dmLoader; +} + +Object.defineProperties(B3dmLoader.prototype, { + /** + * A promise that resolves to the resource when the resource is ready. + * + * @memberof B3dmLoader.prototype + * + * @type {Promise.} + * @readonly + * @private + */ + promise: { + get: function () { + return this._promise.promise; + }, + }, + + /** + * A promise that resolves when all textures are loaded. + * When incrementallyLoadTextures is true this may resolve after + * promise resolves. + * + * @memberof GltfLoader.prototype + * + * @type {Promise} + * @readonly + * @private + */ + texturesLoadedPromise: { + get: function () { + return this._texturesLoadedPromise.promise; + }, + }, + /** + * The cache key of the resource + * + * @memberof B3dmLoader.prototype + * + * @type {String} + * @readonly + * @private + */ + cacheKey: { + get: function () { + return undefined; + }, + }, + /** + * The loaded components. + * + * @memberof B3dmLoader.prototype + * + * @type {ModelComponents.Components} + * @readonly + * @private + */ + components: { + get: function () { + return this._components; + }, + }, + /** + * The RTC transform stored in the feature table. + * + * @memberof B3dmLoader.prototype + * + * @type {Matrix4} + * @readonly + * @private + */ + rtcTransform: { + get: function () { + return this._rtcTransform; + }, + }, +}); + +/** + * Loads the resource. + * @private + */ +B3dmLoader.prototype.load = function () { + var byteStart = this._byteOffset; + var offset = byteStart; + var arrayBuffer = this._arrayBuffer; + var typedArray = new Uint8Array(arrayBuffer); + + // Parse B3DM header. + var header = parseHeader(arrayBuffer); + offset += header.headerByteLength; + var byteLength = header.byteLength; + var batchLength = header.batchLength; + var batchTableJsonByteLength = header.batchTableJsonByteLength; + var batchTableBinaryByteLength = header.batchTableBinaryByteLength; + var featureTableJsonByteLength = header.featureTableJsonByteLength; + var featureTableBinaryByteLength = header.featureTableBinaryByteLength; + + // Load feature table. + var featureTable = loadFeatureTable( + arrayBuffer, + offset, + typedArray, + batchLength, + featureTableJsonByteLength, + featureTableBinaryByteLength + ); + offset += featureTableJsonByteLength + featureTableBinaryByteLength; + this._featureTable = featureTable; + + // Set batch length. + batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); + featureTable.featuresLength = batchLength; + this._batchLength = batchLength; + + // Set RTC transform. + var rtcTransform = Matrix4.IDENTITY; + var rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3 + ); + if (defined(rtcCenter)) { + rtcTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); + this._rtcTransform = rtcTransform; + } + + // Load batch table. + var batchTable = loadBatchTable( + arrayBuffer, + offset, + typedArray, + batchTableJsonByteLength, + batchTableBinaryByteLength + ); + offset += batchTableJsonByteLength + batchTableBinaryByteLength; + this._batchTable = batchTable; + + // Load glTF. + var gltfByteLength = byteStart + byteLength - offset; + if (gltfByteLength === 0) { + throw new RuntimeError("glTF byte length must be greater than 0."); + } + var gltfTypedArray; + if (offset % 4 === 0) { + gltfTypedArray = new Uint8Array(arrayBuffer, offset, gltfByteLength); + } else { + // Create a copy of the glb so that it is 4-byte aligned + B3dmLoader._deprecationWarning( + "b3dm-glb-unaligned", + "The embedded glb is not aligned to a 4-byte boundary." + ); + gltfTypedArray = new Uint8Array( + typedArray.subarray(offset, offset + gltfByteLength) + ); + } + + var gltfLoader = new GltfLoader({ + upAxis: this._upAxis, + typedArray: gltfTypedArray, + forwardAxis: this._forwardAxis, + gltfResource: this._b3dmResource, + baseResource: this._baseResource, + releaseGltfJson: this._releaseGltfJson, + incrementallyLoadTextures: this._incrementallyLoadTextures, + }); + + this._gltfLoader = gltfLoader; + this._state = B3dmLoaderState.LOADING; + + gltfLoader.load(); +}; + +function handleError(gltfLoader, error) { + gltfLoader.unload(); + gltfLoader._state = B3dmLoaderState.FAILED; + var errorMessage = "Failed to load B3DM"; + error = gltfLoader.getError(errorMessage, error); + gltfLoader._promise.reject(error); +} + +function parseHeader(arrayBuffer) { + var offset = 0; + var dataView = new DataView(arrayBuffer); + + // Skip B3DM magic. + offset += UINT32_BYTE_SIZE; + var version = dataView.getUint32(offset, true); + if (version !== 1) { + throw new RuntimeError( + "Only Batched 3D Model version 1 is supported. Version " + + version + + " is not." + ); + } + offset += UINT32_BYTE_SIZE; + + var byteLength = dataView.getUint32(offset, true); + offset += UINT32_BYTE_SIZE; + var featureTableJsonByteLength = dataView.getUint32(offset, true); + offset += UINT32_BYTE_SIZE; + var featureTableBinaryByteLength = dataView.getUint32(offset, true); + offset += UINT32_BYTE_SIZE; + var batchTableJsonByteLength = dataView.getUint32(offset, true); + offset += UINT32_BYTE_SIZE; + var batchTableBinaryByteLength = dataView.getUint32(offset, true); + offset += UINT32_BYTE_SIZE; + + var batchLength; + // Legacy header #1: [batchLength] [batchTableByteLength] + // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] + // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] + // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. + // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. + // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead + if (batchTableJsonByteLength >= 570425344) { + // First legacy check + offset -= UINT32_BYTE_SIZE * 2; + batchLength = featureTableJsonByteLength; + batchTableJsonByteLength = featureTableBinaryByteLength; + batchTableBinaryByteLength = 0; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + B3dmLoader._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } else if (batchTableBinaryByteLength >= 570425344) { + // Second legacy check + offset -= UINT32_BYTE_SIZE; + batchLength = batchTableJsonByteLength; + batchTableJsonByteLength = featureTableJsonByteLength; + batchTableBinaryByteLength = featureTableBinaryByteLength; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + B3dmLoader._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } + + return { + headerByteLength: offset, + byteLength: byteLength, + batchLength: batchLength, + batchTableJsonByteLength: batchTableJsonByteLength, + batchTableBinaryByteLength: batchTableBinaryByteLength, + featureTableJsonByteLength: featureTableJsonByteLength, + featureTableBinaryByteLength: featureTableBinaryByteLength, + }; +} + +function loadFeatureTable( + arrayBuffer, + offset, + typedArray, + batchLength, + featureTableJsonByteLength, + featureTableBinaryByteLength +) { + var featureTableJson; + if (featureTableJsonByteLength === 0) { + featureTableJson = { + BATCH_LENGTH: defaultValue(batchLength, 0), + }; + } else { + featureTableJson = getJsonFromTypedArray( + typedArray, + offset, + featureTableJsonByteLength + ); + offset += featureTableJsonByteLength; + } + + var featureTableBinary = new Uint8Array( + arrayBuffer, + offset, + featureTableBinaryByteLength + ); + offset += featureTableBinaryByteLength; + + return new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); +} + +function loadBatchTable( + arrayBuffer, + offset, + typedArray, + batchTableJsonByteLength, + batchTableBinaryByteLength +) { + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the + // arraybuffer/string compressed in memory and then decompress it when it is first accessed. + // + // We could also make another request for it, but that would make the property set/get + // API async, and would double the number of numbers in some cases. + batchTableJson = getJsonFromTypedArray( + typedArray, + offset, + batchTableJsonByteLength + ); + offset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array( + arrayBuffer, + offset, + batchTableBinaryByteLength + ); + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + offset += batchTableBinaryByteLength; + } + } + + return { + json: batchTableJson, + binary: batchTableBinary, + }; +} + +B3dmLoader.prototype.process = function (frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + var gltfLoader = this._gltfLoader; + + // Once the components are loaded, add the feature metadata created from the batch table. + var that = this; + gltfLoader.process(frameState); + gltfLoader.promise.then(function () { + if (that.isDestroyed()) { + return; + } + + var components = gltfLoader.components; + processGltfComponents(that, components); + that._components = components; + + that._state = B3dmLoader.READY; + that._promise.resolve(that); + }); + + gltfLoader.texturesLoadedPromise + .then(function () { + if (that.isDestroyed()) { + return; + } + + that._texturesLoadedPromise.resolve(that); + }) + .otherwise(function (error) { + if (that.isDestroyed()) { + return; + } + handleError(that, error); + }); +}; + +function processGltfComponents(loader, components) { + var batchTable = loader._batchTable; + var batchLength = loader._batchLength; + var featureTable = loader._featureTable; + + // Add the feature metadata from the batch table to the model components. + if (defined(batchTable.json)) { + components.featureMetadata = parseBatchTable({ + count: batchLength, + batchTable: batchTable.json, + binaryBody: batchTable.binary, + }); + } + + // Apply the RTC Center transform, if present. + var rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3 + ); + if (defined(rtcCenter)) { + var rootNodes = components.scene.nodes; + for (var i = 0; i < rootNodes.length; i++) { + applyRTCTransform(rootNodes[i], rtcCenter); + } + } +} + +function applyRTCTransform(node, rtcCenter) { + if (defined(node.matrix)) { + var rtcTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); + Matrix4.multiply(node.matrix, rtcTransform, node.matrix); + } else if (defined(node.translation)) { + Cartesian3.add(rtcCenter, node.translation, node.translation); + } else { + node.translation = rtcCenter; + } +} + +B3dmLoader.prototype.unload = function () { + if (defined(this._gltfLoader)) { + this._gltfLoader.unload(); + } + + this._components = undefined; +}; + +// This can be overridden for testing purposes +B3dmLoader._deprecationWarning = deprecationWarning; + +export default B3dmLoader; diff --git a/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js b/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js index ac61c33ddcb3..92c808932eb6 100644 --- a/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js +++ b/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js @@ -31,14 +31,7 @@ BatchTexturePipelineStage.process = function ( var batchTextureUniforms = {}; var model = renderResources.model; - var featureTable; - - var content = model.content; - if (defined(content)) { - featureTable = content.featureTables[renderResources.featureTableId]; - } else { - featureTable = model.featureTables[renderResources.featureTableId]; - } + var featureTable = model.featureTables[model.featureTableId]; // Number of features in the feature table. var featuresLength = featureTable.featuresLength; diff --git a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js index 6f949aded007..f2c669ac9129 100644 --- a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js +++ b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js @@ -4,6 +4,7 @@ import defined from "../../Core/defined.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import Buffer from "../../Renderer/Buffer.js"; import BufferUsage from "../../Renderer/BufferUsage.js"; +import MetadataClass from "../MetadataClass.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; import FeatureStageCommon from "../../Shaders/ModelExperimental/FeatureStageCommon.js"; @@ -103,9 +104,7 @@ function processFeatureIdAttributes(renderResources, frameState, primitive) { var featureIdAttributeSetIndex; // For 3D Tiles 1.0, the FEATURE_ID vertex attribute is present but the Feature ID attribute is not. - // The featureMetadata is owned by the Cesium3DTileContent for the legacy formats. - var content = model.content; - if (defined(content) && defined(content.featureMetadata)) { + if (model.featureTableId === MetadataClass.BATCH_TABLE_CLASS_NAME) { featureIdAttributePrefix = "a_featureId"; featureIdAttributeSetIndex = ""; } else { diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 8370353849cd..3b6df9c83d04 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -11,8 +11,8 @@ import when from "../../ThirdParty/when.js"; import destroyObject from "../../Core/destroyObject.js"; import Matrix4 from "../../Core/Matrix4.js"; import ModelFeatureTable from "./ModelFeatureTable.js"; -import Cesium3DTileContentFeatureTable from "./Cesium3DTileContentFeatureTable.js"; import MetadataClass from "../MetadataClass.js"; +import B3dmLoader from "./B3dmLoader.js"; /** * A 3D model. This is a new architecture that is more decoupled than the older {@link Model}. This class is still experimental. @@ -96,27 +96,6 @@ export default function ModelExperimental(options) { initialize(this); } -function createContentFeatureTables(content, featureMetadata) { - var contentFeatureTables = {}; - - var featureTables = featureMetadata.featureTables; - for (var featureTableId in featureTables) { - if (featureTables.hasOwnProperty(featureTableId)) { - var featureTable = featureTables[featureTableId]; - var contentFeatureTable = new Cesium3DTileContentFeatureTable({ - content: content, - featureTable: featureTable, - }); - - if (contentFeatureTable.featuresLength > 0) { - contentFeatureTables[featureTableId] = contentFeatureTable; - } - } - } - - return contentFeatureTables; -} - function createModelFeatureTables(model, featureMetadata) { var modelFeatureTables = {}; @@ -139,9 +118,11 @@ function createModelFeatureTables(model, featureMetadata) { return modelFeatureTables; } -function selectFeatureTableId(components, model, content) { +function selectFeatureTableId(components, model) { + var content = model._content; + // For 3D Tiles 1.0 formats, the feature table always has the "_batchTable" feature table. - if (defined(content) && defined(content.featureMetadata)) { + if (defined(content)) { return MetadataClass.BATCH_TABLE_CLASS_NAME; } @@ -196,25 +177,16 @@ function initialize(model) { .then(function (loader) { var components = loader.components; var content = model._content; - - // For 3D Tiles 1.0 formats, the feature metadata is owned by the Cesium3DTileContent classes. - // Otherwise, the metadata is owned by ModelExperimental. - var hasContent = defined(content); - var featureTableOwner = hasContent ? content : model; - var featureMetadata = defined(featureTableOwner.featureMetadata) - ? content.featureMetadata - : components.featureMetadata; + var featureMetadata = components.featureMetadata; if (defined(featureMetadata) && featureMetadata.featureTableCount > 0) { - var featureTableId = selectFeatureTableId(components, model, content); - var featureTables; - if (hasContent) { - featureTables = createContentFeatureTables(content, featureMetadata); - } else { - featureTables = createModelFeatureTables(model, featureMetadata); - } - featureTableOwner.featureTables = featureTables; - featureTableOwner.featureTableId = featureTableId; + model._featureTables = createModelFeatureTables(model, featureMetadata); + // Select the feature table based on the user-defined featureIdAttribute/featureIdTexture index properties. + model._featureTableId = selectFeatureTableId( + components, + model, + content + ); } model._sceneGraph = new ModelExperimentalSceneGraph({ @@ -698,6 +670,37 @@ ModelExperimental.fromGltf = function (options) { return model; }; +ModelExperimental.fromB3dm = function (options) { + var loaderOptions = { + b3dmResource: options.resource, + arrayBuffer: options.arrayBuffer, + byteOffset: options.byteOffset, + releaseGltfJson: options.releaseGltfJson, + incrementallyLoadTextures: options.incrementallyLoadTextures, + upAxis: options.upAxis, + forwardAxis: options.forwardAxis, + }; + + var loader = new B3dmLoader(loaderOptions); + + var modelOptions = { + loader: loader, + resource: loaderOptions.b3dmResource, + modelMatrix: options.modelMatrix, + debugShowBoundingVolume: options.debugShowBoundingVolume, + cull: options.cull, + opaquePass: options.opaquePass, + allowPicking: options.allowPicking, + customShader: options.customShader, + content: options.content, + show: options.show, + featureIdAttributeIndex: options.featureIdAttributeIndex, + featureIdTextureIndex: options.featureIdTextureIndex, + }; + var model = new ModelExperimental(modelOptions); + return model; +}; + function updateShowBoundingVolume(sceneGraph, debugShowBoundingVolume) { var drawCommands = sceneGraph._drawCommands; for (var i = 0; i < drawCommands.length; i++) { diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index d8912464553d..dd1918136a71 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -14,15 +14,12 @@ import RuntimeError from "../../Core/RuntimeError.js"; export default function ModelExperimental3DTileContent( tileset, tile, - resource, - gltf, - featureMetadata + resource ) { this._tileset = tileset; this._tile = tile; this._resource = resource; - - initialize(this, gltf, featureMetadata); + this._groupMetadata = undefined; } Object.defineProperties(ModelExperimental3DTileContent.prototype, { @@ -108,27 +105,6 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { }, }); -function initialize(content, gltf) { - var tileset = content._tileset; - var tile = content._tile; - var resource = content._resource; - - var modelOptions = { - gltf: gltf, - cull: false, // The model is already culled by 3D Tiles - releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory - opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass - basePath: resource, - modelMatrix: tile.computedTransform, - upAxis: tileset._gltfUpAxis, - forwardAxis: Axis.X, - incrementallyLoadTextures: false, - customShader: tileset.customShader, - content: content, - }; - content._model = ModelExperimental.fromGltf(modelOptions); -} - ModelExperimental3DTileContent.prototype.getFeature = function (featureId) { if (!defined(this._featureTable)) { return undefined; @@ -182,10 +158,24 @@ ModelExperimental3DTileContent.fromGltf = function ( resource, gltf ) { - return new ModelExperimental3DTileContent(tileset, tile, resource, gltf); -}; + var content = new ModelExperimental3DTileContent(tileset, tile, resource); -var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; + var modelOptions = { + gltf: gltf, + cull: false, // The model is already culled by 3D Tiles + releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory + opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass + basePath: resource, + modelMatrix: tile.computedTransform, + upAxis: tileset._gltfUpAxis, + forwardAxis: Axis.X, + incrementallyLoadTextures: false, + customShader: tileset.customShader, + content: content, + }; + content._model = ModelExperimental.fromGltf(modelOptions); + return content; +}; ModelExperimental3DTileContent.fromB3dm = function ( tileset, @@ -194,169 +184,24 @@ ModelExperimental3DTileContent.fromB3dm = function ( arrayBuffer, byteOffset ) { - var byteStart = defaultValue(byteOffset, 0); - byteOffset = byteStart; - var dataView = new DataView(arrayBuffer); - // Skip B3DM magic. - byteOffset += UINT32_BYTE_SIZE; - var version = dataView.getUint32(byteOffset, true); - if (version !== 1) { - throw new RuntimeError( - "Only Batched 3D Model version 1 is supported. Version " + - version + - " is not." - ); - } - byteOffset += UINT32_BYTE_SIZE; - - var byteLength = dataView.getUint32(byteOffset, true); - byteOffset += UINT32_BYTE_SIZE; - var featureTableJsonByteLength = dataView.getUint32(byteOffset, true); - byteOffset += UINT32_BYTE_SIZE; - var featureTableBinaryByteLength = dataView.getUint32(byteOffset, true); - byteOffset += UINT32_BYTE_SIZE; - var batchTableJsonByteLength = dataView.getUint32(byteOffset, true); - byteOffset += UINT32_BYTE_SIZE; - var batchTableBinaryByteLength = dataView.getUint32(byteOffset, true); - byteOffset += UINT32_BYTE_SIZE; - - var batchLength; - // Legacy header #1: [batchLength] [batchTableByteLength] - // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] - // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] - // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. - // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. - // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead - if (batchTableJsonByteLength >= 570425344) { - // First legacy check - byteOffset -= UINT32_BYTE_SIZE * 2; - batchLength = featureTableJsonByteLength; - batchTableJsonByteLength = featureTableBinaryByteLength; - batchTableBinaryByteLength = 0; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - ModelExperimental3DTileContent._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } else if (batchTableBinaryByteLength >= 570425344) { - // Second legacy check - byteOffset -= UINT32_BYTE_SIZE; - batchLength = batchTableJsonByteLength; - batchTableJsonByteLength = featureTableJsonByteLength; - batchTableBinaryByteLength = featureTableBinaryByteLength; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - ModelExperimental3DTileContent._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } - - var typedArray = new Uint8Array(arrayBuffer); - - var featureTableJson; - if (featureTableJsonByteLength === 0) { - featureTableJson = { - BATCH_LENGTH: defaultValue(batchLength, 0), - }; - } else { - featureTableJson = getJsonFromTypedArray( - typedArray, - byteOffset, - featureTableJsonByteLength - ); - byteOffset += featureTableJsonByteLength; - } - - var featureTableBinary = new Uint8Array( - arrayBuffer, - byteOffset, - featureTableBinaryByteLength - ); - byteOffset += featureTableBinaryByteLength; + var content = new ModelExperimental3DTileContent(tileset, tile, resource); - var featureTable = new Cesium3DTileFeatureTable( - featureTableJson, - featureTableBinary - ); - - batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); - featureTable.featuresLength = batchLength; - - var batchTableJson; - var batchTableBinary; - if (batchTableJsonByteLength > 0) { - // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the - // arraybuffer/string compressed in memory and then decompress it when it is first accessed. - // - // We could also make another request for it, but that would make the property set/get - // API async, and would double the number of numbers in some cases. - batchTableJson = getJsonFromTypedArray( - typedArray, - byteOffset, - batchTableJsonByteLength - ); - byteOffset += batchTableJsonByteLength; - - if (batchTableBinaryByteLength > 0) { - // Has a batch table binary - batchTableBinary = new Uint8Array( - arrayBuffer, - byteOffset, - batchTableBinaryByteLength - ); - // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed - batchTableBinary = new Uint8Array(batchTableBinary); - byteOffset += batchTableBinaryByteLength; - } - } - - var featureMetadata; - if (batchLength > 0 && defined(batchTableJson)) { - featureMetadata = parseBatchTable({ - count: batchLength, - batchTable: batchTableJson, - binaryBody: batchTableBinary, - }); - } - - var gltfByteLength = byteStart + byteLength - byteOffset; - if (gltfByteLength === 0) { - throw new RuntimeError("glTF byte length must be greater than 0."); - } - - var gltfTypedArray; - if (byteOffset % 4 === 0) { - gltfTypedArray = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); - } else { - // Create a copy of the glb so that it is 4-byte aligned - ModelExperimental3DTileContent._deprecationWarning( - "b3dm-glb-unaligned", - "The embedded glb is not aligned to a 4-byte boundary." - ); - gltfTypedArray = new Uint8Array( - typedArray.subarray(byteOffset, byteOffset + gltfByteLength) - ); - } - - content._rtcCenterTransform = Matrix4.IDENTITY; - var rtcCenter = featureTable.getGlobalProperty( - "RTC_CENTER", - ComponentDatatype.FLOAT, - 3 - ); - if (defined(rtcCenter)) { - content._rtcCenterTransform = Matrix4.fromTranslation( - Cartesian3.fromArray(rtcCenter) - ); - } - - content._contentModelMatrix = Matrix4.multiply( - tile.computedTransform, - content._rtcCenterTransform, - new Matrix4() - ); + var modelOptions = { + arrayBuffer: arrayBuffer, + byteOffset: byteOffset, + resource: resource, + cull: false, // The model is already culled by 3D Tiles + releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory + opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass + modelMatrix: tile.computedTransform, + upAxis: tileset._gltfUpAxis, + forwardAxis: Axis.X, + incrementallyLoadTextures: false, + customShader: tileset.customShader, + content: content, + }; + content._model = ModelExperimental.fromB3dm(modelOptions); + return content; }; // This can be overridden for testing purposes diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index ddd79e6cca91..fe90596ba6ea 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -12,6 +12,7 @@ import LightingPipelineStage from "./LightingPipelineStage.js"; import MaterialPipelineStage from "./MaterialPipelineStage.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import PickingPipelineStage from "./PickingPipelineStage.js"; +import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; /** * In memory representation of a single primitive, that is, a primitive @@ -82,7 +83,6 @@ function initialize(runtimePrimitive) { var primitive = runtimePrimitive.primitive; var node = runtimePrimitive.node; var model = runtimePrimitive.model; - var content = model.content; var customShader = model.customShader; var hasCustomShader = defined(customShader); @@ -125,11 +125,16 @@ function initialize(runtimePrimitive) { } } - var hasContentMetadata = defined(content) && defined(content.featureMetadata); + var hasFeatureIdVertexAttribute = defined( + ModelExperimentalUtility.getAttributeBySemantic( + primitive, + VertexAttributeSemantic.FEATURE_ID + ) + ); + var hasFeatureIds = - defined(primitive.featureIdAttributes[featureIdAttributeIndex]) || - defined(primitive.featureIdTextures[featureIdTextureIndex]) || - hasContentMetadata; + hasFeatureIdVertexAttribute || + defined(primitive.featureIdTextures[featureIdTextureIndex]); if (hasInstancedFeatureIds || hasFeatureIds) { pipelineStages.push(FeatureIdPipelineStage); pipelineStages.push(BatchTexturePipelineStage); diff --git a/Source/Scene/ModelExperimental/ModelFeatureTable.js b/Source/Scene/ModelExperimental/ModelFeatureTable.js index 72f3e1e8ab97..8831c61f25f2 100644 --- a/Source/Scene/ModelExperimental/ModelFeatureTable.js +++ b/Source/Scene/ModelExperimental/ModelFeatureTable.js @@ -1,6 +1,8 @@ import BatchTexture from "../BatchTexture.js"; +import defined from "../../Core/defined.js"; import destroyObject from "../../Core/destroyObject.js"; import ModelFeature from "./ModelFeature.js"; +import Cesium3DTileFeature from "../Cesium3DTileFeature.js"; /** * Manages the {@link ModelFeature}s in a {@link ModelExperimental}. @@ -62,6 +64,9 @@ Object.defineProperties(ModelFeatureTable.prototype, { }); function initialize(modelFeatureTable) { + var content = modelFeatureTable._model.content; + var hasContent = defined(content); + var featuresLength = modelFeatureTable._featureTable.count; if (featuresLength === 0) { return; @@ -69,19 +74,26 @@ function initialize(modelFeatureTable) { var features = new Array(featuresLength); for (var i = 0; i < featuresLength; i++) { - features[i] = new ModelFeature({ - model: modelFeatureTable._model, - featureId: i, - featureTable: modelFeatureTable, - }); + if (hasContent) { + features[i] = new Cesium3DTileFeature(content, i); + } else { + features[i] = new ModelFeature({ + model: modelFeatureTable._model, + featureId: i, + featureTable: modelFeatureTable, + }); + } } modelFeatureTable._features = features; modelFeatureTable._featuresLength = featuresLength; + modelFeatureTable._batchTexture = new BatchTexture({ featuresLength: featuresLength, owner: modelFeatureTable, - statistics: modelFeatureTable._statistics, + statistics: hasContent + ? content.tileset.statistics + : modelFeatureTable._statistics, }); } diff --git a/Source/Scene/ModelExperimental/PickingPipelineStage.js b/Source/Scene/ModelExperimental/PickingPipelineStage.js index fde3937e18cf..18e691df8daf 100644 --- a/Source/Scene/ModelExperimental/PickingPipelineStage.js +++ b/Source/Scene/ModelExperimental/PickingPipelineStage.js @@ -106,14 +106,13 @@ function buildPickObject(renderResources, instanceId) { function processPickTexture(renderResources, primitive, instances) { var model = renderResources.model; - var content = model.content; var featureTableId; var featureIdAttribute; var featureIdAttributeIndex = model.featureIdAttributeIndex; - if (defined(content)) { + if (defined(model.featureTableId)) { // Extract the Feature Table ID from the Cesium3DTileContent. - featureTableId = content.featureTableId; + featureTableId = model.featureTableId; } else if (defined(instances)) { // Extract the Feature Table ID from the instanced Feature ID attributes. featureIdAttribute = instances.featureIdAttributes[featureIdAttributeIndex]; @@ -129,13 +128,7 @@ function processPickTexture(renderResources, primitive, instances) { featureTableId = featureIdAttribute.featureTableId; } - var featureTable; - - if (defined(content)) { - featureTable = content.featureTables[featureTableId]; - } else { - featureTable = model.featureTables[featureTableId]; - } + var featureTable = model.featureTables[featureTableId]; var shaderBuilder = renderResources.shaderBuilder; shaderBuilder.addUniform( diff --git a/Source/Scene/ModelExperimental/buildDrawCommand.js b/Source/Scene/ModelExperimental/buildDrawCommand.js index afdabd5ed07d..0cc0621c3992 100644 --- a/Source/Scene/ModelExperimental/buildDrawCommand.js +++ b/Source/Scene/ModelExperimental/buildDrawCommand.js @@ -53,6 +53,6 @@ export default function buildDrawCommand(primitiveRenderResources, frameState) { pickId: primitiveRenderResources.pickId, instanceCount: primitiveRenderResources.instanceCount, primitiveType: primitiveRenderResources.primitiveType, - debugShowBoundingVolume: model.debugShowBoundingVolume, + debugShowBoundingVolume: true, }); } From 7adc4a49645e6236ca9d4b1feea3e09d9e7ec5a9 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 13 Oct 2021 15:38:30 -0400 Subject: [PATCH 03/31] Adds generation of empty feature table in B3dmLoader --- Source/Scene/ModelExperimental/B3dmLoader.js | 20 ++++++- .../BatchTexturePipelineStage.js | 1 - .../FeatureIdPipelineStage.js | 3 +- .../ModelExperimental3DTileContent.js | 60 +++++++++++++------ .../ModelExperimentalPrimitive.js | 21 ++++--- 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 86e7e441f956..8bd0da722e10 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -6,12 +6,15 @@ import ComponentDatatype from "../../Core/ComponentDatatype.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import deprecationWarning from "../../Core/deprecationWarning.js"; +import FeatureTable from "../FeatureTable.js"; +import FeatureMetadata from "../FeatureMetadata.js"; import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; import GltfLoader from "../GltfLoader.js"; import parseBatchTable from "../parseBatchTable.js"; import ResourceLoader from "../ResourceLoader.js"; import RuntimeError from "../../Core/RuntimeError.js"; import when from "../../ThirdParty/when.js"; +import MetadataClass from "../MetadataClass.js"; import Matrix4 from "../../Core/Matrix4.js"; var B3dmLoaderState = { @@ -465,14 +468,27 @@ function processGltfComponents(loader, components) { var batchLength = loader._batchLength; var featureTable = loader._featureTable; - // Add the feature metadata from the batch table to the model components. + var featureMetadata; if (defined(batchTable.json)) { - components.featureMetadata = parseBatchTable({ + // Add the feature metadata from the batch table to the model components. + featureMetadata = parseBatchTable({ count: batchLength, batchTable: batchTable.json, binaryBody: batchTable.binary, }); + } else { + // If batch table is not defined, create a feature table without any properties. + var emptyFeatureTable = new FeatureTable({ + count: batchLength, + }); + var featureTables = {}; + featureTables[MetadataClass.BATCH_TABLE_CLASS_NAME] = emptyFeatureTable; + featureMetadata = new FeatureMetadata({ + schema: {}, + featureTables: featureTables, + }); } + components.featureMetadata = featureMetadata; // Apply the RTC Center transform, if present. var rtcCenter = featureTable.getGlobalProperty( diff --git a/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js b/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js index 92c808932eb6..d68ff35b7c0a 100644 --- a/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js +++ b/Source/Scene/ModelExperimental/BatchTexturePipelineStage.js @@ -1,6 +1,5 @@ import combine from "../../Core/combine.js"; import defaultValue from "../../Core/defaultValue.js"; -import defined from "../../Core/defined.js"; /** * The batch texture stage is responsible for setting up the batch texture for the primitive. diff --git a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js index f2c669ac9129..ff1ed9c85060 100644 --- a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js +++ b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js @@ -4,7 +4,6 @@ import defined from "../../Core/defined.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import Buffer from "../../Renderer/Buffer.js"; import BufferUsage from "../../Renderer/BufferUsage.js"; -import MetadataClass from "../MetadataClass.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; import FeatureStageCommon from "../../Shaders/ModelExperimental/FeatureStageCommon.js"; @@ -104,7 +103,7 @@ function processFeatureIdAttributes(renderResources, frameState, primitive) { var featureIdAttributeSetIndex; // For 3D Tiles 1.0, the FEATURE_ID vertex attribute is present but the Feature ID attribute is not. - if (model.featureTableId === MetadataClass.BATCH_TABLE_CLASS_NAME) { + if (primitive.featureIdAttributes.length === 0) { featureIdAttributePrefix = "a_featureId"; featureIdAttributeSetIndex = ""; } else { diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index dd1918136a71..defa01fdd5df 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -1,16 +1,21 @@ import Axis from "../Axis.js"; -import Cesium3DTileFeatureTable from "../Cesium3DTileFeatureTable.js"; -import Color from "../../Core/Color.js"; -import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import deprecationWarning from "../../Core/deprecationWarning.js"; import destroyObject from "../../Core/destroyObject.js"; -import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; import ModelExperimental from "./ModelExperimental.js"; -import parseBatchTable from "../parseBatchTable.js"; import Pass from "../../Renderer/Pass.js"; -import RuntimeError from "../../Core/RuntimeError.js"; +/** + * Represents the contents of a glTF, glb or + * {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel|Batched 3D Model} + * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset. + *

+ * Implements the {@link Cesium3DTileContent} interface. + *

+ * @alias ModelExperimental3DTileContent + * @constructor + * @private + */ export default function ModelExperimental3DTileContent( tileset, tile, @@ -19,37 +24,41 @@ export default function ModelExperimental3DTileContent( this._tileset = tileset; this._tile = tile; this._resource = resource; + + this._model = undefined; this._groupMetadata = undefined; } Object.defineProperties(ModelExperimental3DTileContent.prototype, { featuresLength: { get: function () { - return 0; + var model = this._model; + var featureTable = model.featureTables[model.featureTableId]; + return featureTable.featuresLength; }, }, pointsLength: { get: function () { - return this._model.pointsLength; + return 0; }, }, trianglesLength: { get: function () { - return this._model.trianglesLength; + return 0; }, }, geometryByteLength: { get: function () { - return this._model.geometryByteLength; + return 0; }, }, texturesByteLength: { get: function () { - return this._model.texturesByteLength; + return 0; }, }, @@ -91,7 +100,9 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { batchTable: { get: function () { - return this._featureTable; + var model = this._model; + var featureTable = model.featureTables[model.featureTableId]; + return featureTable; }, }, @@ -106,28 +117,41 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { }); ModelExperimental3DTileContent.prototype.getFeature = function (featureId) { - if (!defined(this._featureTable)) { + var model = this._model; + var featureTables = model.featureTables; + var featureTableId = model.featureTableId; + var featureTable = featureTables[featureTableId]; + + if (!defined(featureTable)) { return undefined; } - return this._featureTable.getFeature(featureId); + return featureTable.getFeature(featureId); }; ModelExperimental3DTileContent.prototype.hasProperty = function ( featureId, name ) { - if (!defined(this._featureTable)) { + var model = this._model; + var featureTables = model.featureTables; + var featureTableId = model.featureTableId; + var featureTable = featureTables[featureTableId]; + + if (!defined(featureTable)) { return false; } - return this._featureTable.hasProperty(featureId, name); + return featureTable.hasProperty(featureId, name); }; ModelExperimental3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) { - color = enabled ? color : Color.WHITE; - this._model.color = color; + return; +}; + +ModelExperimental3DTileContent.prototype.applyStyle = function (style) { + return; }; ModelExperimental3DTileContent.prototype.update = function ( diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index fe90596ba6ea..233e1c398a4c 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -111,31 +111,38 @@ function initialize(runtimePrimitive) { pipelineStages.push(LightingPipelineStage); + // The FeatureIdPipelineStage and BatchTexturePipelineStage when the primitive has features, i.e. when at least one of the following conditions exists: + // - the node is instanced and has feature ID attributes + // - the primitive has a feature ID vertex attributes + // - the primitive has a feature ID texture + // It must be noted that we check for the presence of feature ID vertex attributes, and not feature ID attributes, because it is possible to have features in a model + // without a feature table (for example, in 3D Tiles 1.0, where batch length > 0 but a batch table is not defined.) var featureIdAttributeIndex = model.featureIdAttributeIndex; var featureIdTextureIndex = model.featureIdTextureIndex; - - var hasInstancedFeatureIds; + var hasInstancedFeatureIdAttribute; if ( defined(node.instances) && node.instances.featureIdAttributes.length > 0 ) { var featureIdAttributes = node.instances.featureIdAttributes; if (defined(featureIdAttributes[featureIdAttributeIndex])) { - hasInstancedFeatureIds = true; + hasInstancedFeatureIdAttribute = true; } } - var hasFeatureIdVertexAttribute = defined( ModelExperimentalUtility.getAttributeBySemantic( primitive, VertexAttributeSemantic.FEATURE_ID ) ); - + var hasFeatureIdTexture = defined( + primitive.featureIdTextures[featureIdTextureIndex] + ); var hasFeatureIds = + hasInstancedFeatureIdAttribute || hasFeatureIdVertexAttribute || - defined(primitive.featureIdTextures[featureIdTextureIndex]); - if (hasInstancedFeatureIds || hasFeatureIds) { + hasFeatureIdTexture; + if (hasInstancedFeatureIdAttribute || hasFeatureIds) { pipelineStages.push(FeatureIdPipelineStage); pipelineStages.push(BatchTexturePipelineStage); } From 075a43dbc654417b0d543038d386c88166d1d546 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 13 Oct 2021 15:45:20 -0400 Subject: [PATCH 04/31] Doc fix [skip-ci] --- Source/Scene/ModelExperimental/B3dmLoader.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 8bd0da722e10..7242314ab2e8 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -37,6 +37,7 @@ var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; * @alias B3dmLoader * @constructor * @augments ResourceLoader + * @private * * @param {Object} options Object with the following properties: * @param {Resource} options.b3dmResource The {@link Resource} containing the B3DM. @@ -189,7 +190,9 @@ B3dmLoader.prototype.load = function () { // Parse B3DM header. var header = parseHeader(arrayBuffer); + // The length of the B3DM header. offset += header.headerByteLength; + // The byte length of the B3DM. var byteLength = header.byteLength; var batchLength = header.batchLength; var batchTableJsonByteLength = header.batchTableJsonByteLength; @@ -214,18 +217,6 @@ B3dmLoader.prototype.load = function () { featureTable.featuresLength = batchLength; this._batchLength = batchLength; - // Set RTC transform. - var rtcTransform = Matrix4.IDENTITY; - var rtcCenter = featureTable.getGlobalProperty( - "RTC_CENTER", - ComponentDatatype.FLOAT, - 3 - ); - if (defined(rtcCenter)) { - rtcTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); - this._rtcTransform = rtcTransform; - } - // Load batch table. var batchTable = loadBatchTable( arrayBuffer, From 55d74d7ccc7d650f6abfb56e33dced6cf36042fa Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 13 Oct 2021 16:04:30 -0400 Subject: [PATCH 05/31] Reverts B3DM and Gltf Cesium3DTileContent classes to old versions without ModelExperimental --- Source/Scene/Batched3DModel3DTileContent.js | 174 +++++--------------- Source/Scene/Gltf3DTileContent.js | 118 +++---------- 2 files changed, 69 insertions(+), 223 deletions(-) diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index b481980e5750..3bf8ef3db025 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -1,13 +1,11 @@ import Cartesian3 from "../Core/Cartesian3.js"; import Color from "../Core/Color.js"; -import combine from "../Core/combine.js"; import ComponentDatatype from "../Core/ComponentDatatype.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import deprecationWarning from "../Core/deprecationWarning.js"; import destroyObject from "../Core/destroyObject.js"; import DeveloperError from "../Core/DeveloperError.js"; -import ExperimentalFeatures from "../Core/ExperimentalFeatures.js"; import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js"; import Matrix4 from "../Core/Matrix4.js"; import RequestType from "../Core/RequestType.js"; @@ -20,9 +18,7 @@ import Cesium3DTileFeatureTable from "./Cesium3DTileFeatureTable.js"; import ClassificationModel from "./ClassificationModel.js"; import Model from "./Model.js"; import ModelAnimationLoop from "./ModelAnimationLoop.js"; -import ModelExperimental from "./ModelExperimental/ModelExperimental.js"; import ModelUtility from "./ModelUtility.js"; -import parseBatchTable from "./parseBatchTable.js"; /** * Represents the contents of a @@ -65,11 +61,6 @@ function Batched3DModel3DTileContent( this.featurePropertiesDirty = false; this._groupMetadata = undefined; - this._featureMetadata = undefined; - this._featureTables = []; - this._featureTableId = undefined; - this._featureTable = undefined; - initialize(this, arrayBuffer, byteOffset); } @@ -79,7 +70,7 @@ Batched3DModel3DTileContent._deprecationWarning = deprecationWarning; Object.defineProperties(Batched3DModel3DTileContent.prototype, { featuresLength: { get: function () { - return defined(this.batchTable) ? this.batchTable.featuresLength : 0; + return this.batchTable.featuresLength; }, }, @@ -109,7 +100,7 @@ Object.defineProperties(Batched3DModel3DTileContent.prototype, { batchTableByteLength: { get: function () { - return defined(this.batchTable) ? this.batchTable.memorySizeInBytes : 0; + return this.batchTable.memorySizeInBytes; }, }, @@ -145,43 +136,7 @@ Object.defineProperties(Batched3DModel3DTileContent.prototype, { batchTable: { get: function () { - return ExperimentalFeatures.enableModelExperimental - ? this._featureTable - : this._batchTable; - }, - }, - - /** - * @private - */ - featureMetadata: { - get: function () { - return this._featureMetadata; - }, - }, - - /** - * @private - */ - featureTables: { - get: function () { - return this._featureTables; - }, - set: function (value) { - this._featureTables = value; - }, - }, - - /** - * @private - */ - featureTableId: { - get: function () { - return this._featureTableId; - }, - set: function (value) { - this._featureTableId = value; - this._featureTable = this._featureTables[value]; + return this._batchTable; }, }, @@ -412,28 +367,14 @@ function initialize(content, arrayBuffer, byteOffset) { colorChangedCallback = createColorChangedCallback(content); } - var batchTable; - if ( - ExperimentalFeatures.enableModelExperimental && - batchLength > 0 && - defined(batchTableJson) - ) { - var featureMetadata = parseBatchTable({ - count: batchLength, - batchTable: batchTableJson, - binaryBody: batchTableBinary, - }); - content._featureMetadata = featureMetadata; - } else { - batchTable = new Cesium3DTileBatchTable( - content, - batchLength, - batchTableJson, - batchTableBinary, - colorChangedCallback - ); - content._batchTable = batchTable; - } + var batchTable = new Cesium3DTileBatchTable( + content, + batchLength, + batchTableJson, + batchTableBinary, + colorChangedCallback + ); + content._batchTable = batchTable; var gltfByteLength = byteStart + byteLength - byteOffset; if (gltfByteLength === 0) { @@ -478,53 +419,39 @@ function initialize(content, arrayBuffer, byteOffset) { ); if (!defined(content._classificationType)) { - var modelOptions = { + // PERFORMANCE_IDEA: patch the shader on demand, e.g., the first time show/color changes. + // The pick shader still needs to be patched. + content._model = new Model({ gltf: gltfView, cull: false, // The model is already culled by 3D Tiles releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass basePath: resource, + requestType: RequestType.TILES3D, modelMatrix: content._contentModelMatrix, upAxis: tileset._gltfUpAxis, forwardAxis: Axis.X, + shadows: tileset.shadows, + debugWireframe: tileset.debugWireframe, incrementallyLoadTextures: false, - }; - - if (ExperimentalFeatures.enableModelExperimental) { - modelOptions.content = content; - modelOptions.customShader = tileset.customShader; - modelOptions.content = content; - content._model = ModelExperimental.fromGltf(modelOptions); - } else { - modelOptions = combine(modelOptions, { - requestType: RequestType.TILES3D, - shadows: tileset.shadows, - debugWireframe: tileset.debugWireframe, - vertexShaderLoaded: getVertexShaderCallback(content), - fragmentShaderLoaded: getFragmentShaderCallback(content), - uniformMapLoaded: batchTable.getUniformMapCallback(), - pickIdLoaded: getPickIdCallback(content), - addBatchIdToGeneratedShaders: batchLength > 0, // If the batch table has values in it, generated shaders will need a batchId attribute - pickObject: pickObject, - imageBasedLightingFactor: tileset.imageBasedLightingFactor, - lightColor: tileset.lightColor, - luminanceAtZenith: tileset.luminanceAtZenith, - sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, - specularEnvironmentMaps: tileset.specularEnvironmentMaps, - backFaceCulling: tileset.backFaceCulling, - showOutline: tileset.showOutline, - }); - // PERFORMANCE_IDEA: patch the shader on demand, e.g., the first time show/color changes. - // The pick shader still needs to be patched. - content._model = new Model(modelOptions); - } - + vertexShaderLoaded: getVertexShaderCallback(content), + fragmentShaderLoaded: getFragmentShaderCallback(content), + uniformMapLoaded: batchTable.getUniformMapCallback(), + pickIdLoaded: getPickIdCallback(content), + addBatchIdToGeneratedShaders: batchLength > 0, // If the batch table has values in it, generated shaders will need a batchId attribute + pickObject: pickObject, + imageBasedLightingFactor: tileset.imageBasedLightingFactor, + lightColor: tileset.lightColor, + luminanceAtZenith: tileset.luminanceAtZenith, + sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, + specularEnvironmentMaps: tileset.specularEnvironmentMaps, + backFaceCulling: tileset.backFaceCulling, + showOutline: tileset.showOutline, + }); content._model.readyPromise.then(function (model) { - if (defined(model.activeAnimations)) { - model.activeAnimations.addAll({ - loop: ModelAnimationLoop.REPEAT, - }); - } + model.activeAnimations.addAll({ + loop: ModelAnimationLoop.REPEAT, + }); }); } else { // This transcodes glTF to an internal representation for geometry so we can take advantage of the re-batching of vector data. @@ -562,17 +489,10 @@ function createFeatures(content) { } Batched3DModel3DTileContent.prototype.hasProperty = function (batchId, name) { - return this.batchTable.hasProperty(batchId, name); + return this._batchTable.hasProperty(batchId, name); }; Batched3DModel3DTileContent.prototype.getFeature = function (batchId) { - if ( - ExperimentalFeatures.enableModelExperimental && - defined(this.batchTable) - ) { - return this.batchTable.getFeature(batchId); - } - //>>includeStart('debug', pragmas.debug); var featuresLength = this.featuresLength; if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { @@ -618,14 +538,12 @@ Batched3DModel3DTileContent.prototype.update = function (tileset, frameState) { var model = this._model; var tile = this._tile; - var batchTable = this.batchTable; + var batchTable = this._batchTable; // In the PROCESSING state we may be calling update() to move forward // the content's resource loading. In the READY state, it will // actually generate commands. - if (defined(batchTable)) { - batchTable.update(tileset, frameState); - } + batchTable.update(tileset, frameState); this._contentModelMatrix = Matrix4.multiply( tile.computedTransform, @@ -668,16 +586,14 @@ Batched3DModel3DTileContent.prototype.update = function (tileset, frameState) { model.update(frameState); - if (!ExperimentalFeatures.enableModelExperimental) { - // If any commands were pushed, add derived commands - var commandEnd = frameState.commandList.length; - if ( - commandStart < commandEnd && - (frameState.passes.render || frameState.passes.pick) && - !defined(this._classificationType) - ) { - batchTable.addDerivedCommands(frameState, commandStart); - } + // If any commands were pushed, add derived commands + var commandEnd = frameState.commandList.length; + if ( + commandStart < commandEnd && + (frameState.passes.render || frameState.passes.pick) && + !defined(this._classificationType) + ) { + batchTable.addDerivedCommands(frameState, commandStart); } }; diff --git a/Source/Scene/Gltf3DTileContent.js b/Source/Scene/Gltf3DTileContent.js index ff7cc5ce5fa1..360a4f56310f 100644 --- a/Source/Scene/Gltf3DTileContent.js +++ b/Source/Scene/Gltf3DTileContent.js @@ -6,14 +6,11 @@ import Pass from "../Renderer/Pass.js"; import Axis from "./Axis.js"; import Model from "./Model.js"; import ModelAnimationLoop from "./ModelAnimationLoop.js"; -import ExperimentalFeatures from "../Core/ExperimentalFeatures.js"; -import ModelExperimental from "./ModelExperimental/ModelExperimental.js"; -import combine from "../Core/combine.js"; /** - * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf|3DTILES_content_gltf} extension. + * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf/0.0.0|3DTILES_content_gltf} extension. *

- * This class does not yet support the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata|EXT_feature_metadata Extension}. + * This class does not yet support the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0|EXT_feature_metadata Extension}. *

*

* Implements the {@link Cesium3DTileContent} interface. @@ -34,10 +31,6 @@ function Gltf3DTileContent(tileset, tile, resource, gltf) { this.featurePropertiesDirty = false; this._groupMetadata = undefined; - this._featureTable = undefined; - this._featureTables = undefined; - this._featureTableId = undefined; - initialize(this, gltf); } @@ -110,7 +103,7 @@ Object.defineProperties(Gltf3DTileContent.prototype, { batchTable: { get: function () { - return this._featureTable; + return undefined; }, }, @@ -122,30 +115,6 @@ Object.defineProperties(Gltf3DTileContent.prototype, { this._groupMetadata = value; }, }, - /** - * @private - */ - featureTables: { - get: function () { - return this._featureTables; - }, - set: function (value) { - this._featureTables = value; - }, - }, - - /** - * @private - */ - featureTableId: { - get: function () { - return this._featureTableId; - }, - set: function (value) { - this._featureTableId = value; - this._featureTable = this._featureTables[value]; - }, - }, }); function initialize(content, gltf) { @@ -158,61 +127,42 @@ function initialize(content, gltf) { primitive: tileset, }; - var modelOptions = { + content._model = new Model({ gltf: gltf, cull: false, // The model is already culled by 3D Tiles releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass basePath: resource, + requestType: RequestType.TILES3D, modelMatrix: tile.computedTransform, upAxis: tileset._gltfUpAxis, forwardAxis: Axis.X, + shadows: tileset.shadows, + debugWireframe: tileset.debugWireframe, incrementallyLoadTextures: false, - }; - - if (ExperimentalFeatures.enableModelExperimental) { - modelOptions.customShader = tileset.customShader; - modelOptions.content = content; - content._model = ModelExperimental.fromGltf(modelOptions); - } else { - modelOptions = combine(modelOptions, { - requestType: RequestType.TILES3D, - shadows: tileset.shadows, - debugWireframe: tileset.debugWireframe, - addBatchIdToGeneratedShaders: false, - pickObject: pickObject, - imageBasedLightingFactor: tileset.imageBasedLightingFactor, - lightColor: tileset.lightColor, - luminanceAtZenith: tileset.luminanceAtZenith, - sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, - specularEnvironmentMaps: tileset.specularEnvironmentMaps, - backFaceCulling: tileset.backFaceCulling, - showOutline: tileset.showOutline, - }); - content._model = new Model(modelOptions); - } - + addBatchIdToGeneratedShaders: false, + pickObject: pickObject, + imageBasedLightingFactor: tileset.imageBasedLightingFactor, + lightColor: tileset.lightColor, + luminanceAtZenith: tileset.luminanceAtZenith, + sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, + specularEnvironmentMaps: tileset.specularEnvironmentMaps, + backFaceCulling: tileset.backFaceCulling, + showOutline: tileset.showOutline, + }); content._model.readyPromise.then(function (model) { - if (defined(model.activeAnimations)) { - model.activeAnimations.addAll({ - loop: ModelAnimationLoop.REPEAT, - }); - } + model.activeAnimations.addAll({ + loop: ModelAnimationLoop.REPEAT, + }); }); } -Gltf3DTileContent.prototype.getFeature = function (featureId) { - if (defined(this.batchTable)) { - return this.batchTable.getFeature(featureId); - } - return undefined; +Gltf3DTileContent.prototype.hasProperty = function (batchId, name) { + return false; }; -Gltf3DTileContent.prototype.hasProperty = function (featureId, name) { - if (defined(this.batchTable)) { - return this.batchTable.hasProperty(featureId, name); - } - return false; +Gltf3DTileContent.prototype.getFeature = function (batchId) { + return undefined; }; Gltf3DTileContent.prototype.applyDebugSettings = function (enabled, color) { @@ -266,16 +216,6 @@ Gltf3DTileContent.prototype.update = function (tileset, frameState) { model._clippingPlanes = tilesetClippingPlanes; } - var featureTables = this._featureTables; - if (defined(featureTables)) { - for (var featureTableId in featureTables) { - if (featureTables.hasOwnProperty(featureTableId)) { - var featureTable = featureTables[featureTableId]; - featureTable.update(tileset, frameState); - } - } - } - model.update(frameState); }; @@ -284,16 +224,6 @@ Gltf3DTileContent.prototype.isDestroyed = function () { }; Gltf3DTileContent.prototype.destroy = function () { - var featureTables = this._featureTables; - if (defined(featureTables)) { - for (var featureTableId in featureTables) { - if (featureTables.hasOwnProperty(featureTableId)) { - var featureTable = featureTables[featureTableId]; - featureTable.destroy(); - } - } - } - this._model = this._model && this._model.destroy(); return destroyObject(this); }; From 853a5e48d09fa785e6ff303993296c4946f52fce Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 10:08:17 -0400 Subject: [PATCH 06/31] doc fix [skip-ci] --- Source/Scene/ModelExperimental/B3dmLoader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 7242314ab2e8..97a2bcda68dd 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -41,8 +41,8 @@ var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; * * @param {Object} options Object with the following properties: * @param {Resource} options.b3dmResource The {@link Resource} containing the B3DM. - * @param {ArrayBuffer} options.arrayBuffer The array buffer containing the B3DM contents. - * @param {Number} [options.byteOffset] The byte offset to the beginning of the B3DM contents in the typed array. + * @param {ArrayBuffer} options.arrayBuffer The array buffer of the B3DM contents. + * @param {Number} [options.byteOffset] The byte offset to the beginning of the B3DM contents in the array buffer. * @param {Resource} [options.baseResource] The {@link Resource} that paths in the glTF JSON are relative to. * @param {Boolean} [options.releaseGltfJson=false] When true, the glTF JSON is released once the glTF is loaded. This is is especially useful for cases like 3D Tiles, where each .gltf model is unique and caching the glTF JSON is not effective. * @param {Boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. From c9492b66722d93741c76ef982ea07044dd605859 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 11:25:17 -0400 Subject: [PATCH 07/31] Removes Cesium3DTileContentFeatureTable --- .../Cesium3DTileContentFeatureTable.js | 182 ------------------ .../Cesium3DTileContentFeatureTableSpec.js | 144 -------------- 2 files changed, 326 deletions(-) delete mode 100644 Source/Scene/ModelExperimental/Cesium3DTileContentFeatureTable.js delete mode 100644 Specs/Scene/ModelExperimental/Cesium3DTileContentFeatureTableSpec.js diff --git a/Source/Scene/ModelExperimental/Cesium3DTileContentFeatureTable.js b/Source/Scene/ModelExperimental/Cesium3DTileContentFeatureTable.js deleted file mode 100644 index 89cd96c86157..000000000000 --- a/Source/Scene/ModelExperimental/Cesium3DTileContentFeatureTable.js +++ /dev/null @@ -1,182 +0,0 @@ -import BatchTexture from "../BatchTexture.js"; -import Cesium3DTileFeature from "../Cesium3DTileFeature.js"; -import Check from "../../Core/Check.js"; -import destroyObject from "../../Core/destroyObject.js"; - -/** - * Manages the {@link Cesium3DTileFeature}s that belong to a {@link Cesium3DTileContent}. - * The properties for a feature are extracted from a {@link FeatureTable}. - * - * @param {Object} options An object containing the following options: - * @param {Cesium3DTileContent} options.content The tile content the features in this table belong to. - * @param {FeatureTable} options.featureTable The feature table from the model belonging to the content. - * - * @alias Cesium3DTileContentFeatureTable - * @constructor - * @private - * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. - */ -export default function Cesium3DTileContentFeatureTable(options) { - var content = options.content; - var featureTable = options.featureTable; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.content", content); - Check.typeOf.object("options.featureTable", featureTable); - //>>includeEnd('debug'); - - this._content = content; - this._featureTable = featureTable; - - this._featuresLength = 0; - this._features = undefined; - - this._batchTexture = undefined; - - initialize(this); -} - -Object.defineProperties(Cesium3DTileContentFeatureTable.prototype, { - /** - * The batch texture generated for the features in this table. - * - * @memberof Cesium3DTileContentFeatureTable.prototype - * @type {BatchTexture} - * @readonly - * @private - */ - batchTexture: { - get: function () { - return this._batchTexture; - }, - }, - /** - * The number of features in this table. - * - * @memberof Cesium3DTileContentFeatureTable.prototype - * @type {Number} - * @readonly - * @private - */ - featuresLength: { - get: function () { - return this._featuresLength; - }, - }, - memorySizeInBytes: { - get: function () { - return this._batchTexture.memorySizeInBytes; - }, - }, -}); - -function initialize(contentFeatureTable) { - var featuresLength = contentFeatureTable._featureTable.count; - if (featuresLength === 0) { - return; - } - - var content = contentFeatureTable._content; - - var features = new Array(featuresLength); - for (var i = 0; i < featuresLength; i++) { - features[i] = new Cesium3DTileFeature(content, i); - } - - contentFeatureTable._featuresLength = featuresLength; - contentFeatureTable._features = features; - - contentFeatureTable._batchTexture = new BatchTexture({ - featuresLength: featuresLength, - owner: content, - statistics: content.tileset.statistics, - }); -} - -Cesium3DTileContentFeatureTable.prototype.getFeature = function (featureId) { - return this._features[featureId]; -}; - -Cesium3DTileContentFeatureTable.prototype.hasProperty = function ( - featureId, - propertyName -) { - return this._featureTable.hasProperty(featureId, propertyName); -}; - -Cesium3DTileContentFeatureTable.prototype.getProperty = function ( - featureId, - name -) { - return this._featureTable.getProperty(featureId, name); -}; - -Cesium3DTileContentFeatureTable.prototype.getPropertyInherited = function ( - featureId, - name -) { - return this._featureTable.getProperty(featureId, name); -}; - -Cesium3DTileContentFeatureTable.prototype.getPropertyNames = function ( - results -) { - return this._featureTable.getPropertyIds(results); -}; - -Cesium3DTileContentFeatureTable.prototype.setProperty = function ( - featureId, - name, - value -) { - return this._featureTable.setProperty(featureId, name, value); -}; - -Cesium3DTileContentFeatureTable.prototype.update = function ( - tileset, - frameState -) { - this._batchTexture.update(tileset, frameState); -}; - -Cesium3DTileContentFeatureTable.prototype.getPickColor = function (featureId) { - return this._batchTexture.getPickColor(featureId); -}; - -/** - * Returns true if this object was destroyed; otherwise, false. - *

- * If this object was destroyed, it should not be used; calling any function other than - * isDestroyed will result in a {@link DeveloperError} exception. - *

- * - * @returns {Boolean} true if this object was destroyed; otherwise, false. - * - * @see Cesium3DTileContentFeatureTable#destroy - * @private - */ -Cesium3DTileContentFeatureTable.prototype.isDestroyed = function () { - return false; -}; - -/** - * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic - * release of WebGL resources, instead of relying on the garbage collector to destroy this object. - *

- * Once an object is destroyed, it should not be used; calling any function other than - * isDestroyed will result in a {@link DeveloperError} exception. Therefore, - * assign the return value (undefined) to the object as done in the example. - *

- * - * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. - * - * @example - * e = e && e.destroy(); - * - * @see Cesium3DTileContentFeatureTable#isDestroyed - * @private - */ -Cesium3DTileContentFeatureTable.prototype.destroy = function (frameState) { - this._batchTexture.destroy(); - destroyObject(this); -}; diff --git a/Specs/Scene/ModelExperimental/Cesium3DTileContentFeatureTableSpec.js b/Specs/Scene/ModelExperimental/Cesium3DTileContentFeatureTableSpec.js deleted file mode 100644 index 0231a24ac840..000000000000 --- a/Specs/Scene/ModelExperimental/Cesium3DTileContentFeatureTableSpec.js +++ /dev/null @@ -1,144 +0,0 @@ -import { - Cesium3DTileContentFeatureTable, - Cesium3DTileFeature, -} from "../../../Source/Cesium.js"; -import MetadataTester from "../../MetadataTester.js"; - -describe("Scene/ModelExperimental/Cesium3DTileContentFeatureTable", function () { - var properties = { - height: { - type: "FLOAT32", - }, - name: { - type: "STRING", - }, - }; - var propertyValues = { - height: [1.0, 2.0], - name: ["A", "B"], - }; - - var mockContent = { - tileset: { - statistics: {}, - }, - }; - var mockFeatureTable = MetadataTester.createFeatureTable({ - properties: properties, - propertyValues: propertyValues, - }); - - it("hasProperty works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - var modelFeatures = table._features; - for (var i = 0; i < modelFeatures.length; i++) { - var feature = modelFeatures[i]; - expect(feature.hasProperty("height")).toEqual(true); - expect(feature.hasProperty("width")).toEqual(false); - } - }); - - it("getFeature works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - expect(table._featuresLength).toEqual(mockFeatureTable.count); - var modelFeatures = table._features; - for (var i = 0; i < modelFeatures.length; i++) { - var feature = table.getFeature(i); - expect(feature).toEqual(modelFeatures[i]); - expect(feature).toBeInstanceOf(Cesium3DTileFeature); - } - }); - - it("getProperty works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - expect(table._featuresLength).toEqual(mockFeatureTable.count); - var modelFeatures = table._features; - - for (var propertyName in properties) { - if (properties.hasOwnProperty(propertyName)) { - for (var i = 0; i < modelFeatures.length; i++) { - var feature = modelFeatures[i]; - expect(feature.getProperty(propertyName)).toEqual( - propertyValues[propertyName][i] - ); - } - } - } - }); - - it("getPropertyInherited works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - expect(table._featuresLength).toEqual(mockFeatureTable.count); - var modelFeatures = table._features; - - for (var propertyName in properties) { - if (properties.hasOwnProperty(propertyName)) { - for (var i = 0; i < modelFeatures.length; i++) { - var feature = modelFeatures[i]; - expect(feature.getPropertyInherited(propertyName)).toEqual( - propertyValues[propertyName][i] - ); - } - } - } - }); - - it("getPropertyNames works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - var modelFeatures = table._features; - var results; - for (var i = 0; i < modelFeatures.length; i++) { - results = []; - var feature = modelFeatures[i]; - expect(feature.getPropertyNames(results)).toEqual(["height", "name"]); - } - }); - - it("setProperty works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - var feature = table._features[0]; - expect(feature.getProperty("height")).toEqual(1.0); - feature.setProperty("height", 3.0); - expect(feature.getProperty("height")).toEqual(3.0); - }); - - it("destroy works", function () { - var table = new Cesium3DTileContentFeatureTable({ - content: mockContent, - featureTable: mockFeatureTable, - }); - mockContent.batchTable = table; - var batchTexture = table._batchTexture; - expect(batchTexture.isDestroyed()).toEqual(false); - expect(table.isDestroyed()).toEqual(false); - - table.destroy(); - - expect(batchTexture.isDestroyed()).toEqual(true); - expect(table.isDestroyed()).toEqual(true); - }); -}); From d606b7acbd22f1bcc187e321c10967fb4be84df5 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 11:26:58 -0400 Subject: [PATCH 08/31] Removes unused tests from specs --- .../Scene/Batched3DModel3DTileContentSpec.js | 70 ----------------- Specs/Scene/Gltf3DTileContentSpec.js | 75 ------------------- 2 files changed, 145 deletions(-) diff --git a/Specs/Scene/Batched3DModel3DTileContentSpec.js b/Specs/Scene/Batched3DModel3DTileContentSpec.js index 38bdc127d6b7..40648969e57a 100644 --- a/Specs/Scene/Batched3DModel3DTileContentSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentSpec.js @@ -1,7 +1,6 @@ import { Cartesian3, Color, - ExperimentalFeatures, HeadingPitchRange, HeadingPitchRoll, Matrix4, @@ -10,8 +9,6 @@ import { Cesium3DTilePass, ClippingPlane, ClippingPlaneCollection, - Cesium3DTileFeature, - Cesium3DTileContentFeatureTable, MetadataClass, GroupMetadata, Model, @@ -461,73 +458,6 @@ describe( return Cesium3DTilesTester.tileDestroys(scene, withoutBatchTableUrl); }); - describe("ModelExperimental", function () { - beforeEach(function () { - ExperimentalFeatures.enableModelExperimental = true; - }); - - afterEach(function () { - ExperimentalFeatures.enableModelExperimental = false; - }); - - it("renders B3DM content with batch table", function () { - return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( - function (tileset) { - Cesium3DTilesTester.expectRender(scene, tileset); - } - ); - }); - - it("renders B3DM content without batch table", function () { - return Cesium3DTilesTester.loadTileset( - scene, - withoutBatchTableUrl - ).then(function (tileset) { - Cesium3DTilesTester.expectRender(scene, tileset); - }); - }); - - it("assigns feature table as batch table", function () { - return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( - function (tileset) { - var content = tileset.root.content; - expect(content.batchTable).toBeDefined(); - expect(content.batchTable).toBeInstanceOf( - Cesium3DTileContentFeatureTable - ); - } - ); - }); - - it("hasProperty works", function () { - return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( - function (tileset) { - var content = tileset.root.content; - var featureTable = content.batchTable; - expect(featureTable).toBeDefined(); - for (var i = 0; i < featureTable.featuresLength; i++) { - expect(content.hasProperty(i, "Height")).toEqual(true); - expect(content.hasProperty(i, "Width")).toEqual(false); - } - } - ); - }); - - it("getFeature works", function () { - return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( - function (tileset) { - var content = tileset.root.content; - var featureTable = content.batchTable; - expect(featureTable).toBeDefined(); - for (var i = 0; i < featureTable.featuresLength; i++) { - var feature = content.getFeature(i); - expect(feature).toBeInstanceOf(Cesium3DTileFeature); - } - } - ); - }); - }); - describe("3DTILES_metadata", function () { var metadataClass = new MetadataClass({ id: "test", diff --git a/Specs/Scene/Gltf3DTileContentSpec.js b/Specs/Scene/Gltf3DTileContentSpec.js index bb2e3c3c4a9b..1396e5aa9f76 100644 --- a/Specs/Scene/Gltf3DTileContentSpec.js +++ b/Specs/Scene/Gltf3DTileContentSpec.js @@ -1,9 +1,6 @@ import { Cartesian3, - Cesium3DTileContentFeatureTable, - Cesium3DTileFeature, Cesium3DTilePass, - ExperimentalFeatures, ClippingPlane, ClippingPlaneCollection, HeadingPitchRange, @@ -23,8 +20,6 @@ describe( var gltfContentUrl = "./Data/Cesium3DTiles/GltfContent/glTF/tileset.json"; var glbContentUrl = "./Data/Cesium3DTiles/GltfContent/glb/tileset.json"; - var buildingsMetadataUrl = - "./Data/Cesium3DTiles/Metadata/FeatureMetadata/tileset.json"; function setCamera(longitude, latitude) { // One feature is located at the center, point the camera there @@ -201,76 +196,6 @@ describe( return Cesium3DTilesTester.tileDestroys(scene, gltfContentUrl); }); - describe("ModelExperimental", function () { - beforeEach(function () { - ExperimentalFeatures.enableModelExperimental = true; - }); - - afterEach(function () { - ExperimentalFeatures.enableModelExperimental = false; - }); - - it("renders glTF content with metadata", function () { - return Cesium3DTilesTester.loadTileset( - scene, - buildingsMetadataUrl - ).then(function (tileset) { - Cesium3DTilesTester.expectRender(scene, tileset); - }); - }); - - it("renders glTF content without metadata", function () { - return Cesium3DTilesTester.loadTileset(scene, glbContentUrl).then( - function (tileset) { - Cesium3DTilesTester.expectRender(scene, tileset); - } - ); - }); - - it("assigns feature table as batch table", function () { - return Cesium3DTilesTester.loadTileset( - scene, - buildingsMetadataUrl - ).then(function (tileset) { - var content = tileset.root.content; - expect(content.batchTable).toBeDefined(); - expect(content.batchTable).toBeInstanceOf( - Cesium3DTileContentFeatureTable - ); - }); - }); - - it("hasProperty works", function () { - return Cesium3DTilesTester.loadTileset( - scene, - buildingsMetadataUrl - ).then(function (tileset) { - var content = tileset.root.content; - var featureTable = content.batchTable; - expect(featureTable).toBeDefined(); - for (var i = 0; i < featureTable.featuresLength; i++) { - expect(content.hasProperty(i, "height")).toEqual(true); - expect(content.hasProperty(i, "width")).toEqual(false); - } - }); - }); - - it("getFeature works", function () { - return Cesium3DTilesTester.loadTileset( - scene, - buildingsMetadataUrl - ).then(function (tileset) { - var content = tileset.root.content; - var featureTable = content.batchTable; - expect(featureTable).toBeDefined(); - for (var i = 0; i < featureTable.featuresLength; i++) { - var feature = content.getFeature(i); - expect(feature).toBeInstanceOf(Cesium3DTileFeature); - } - }); - }); - }); - describe("3DTILES_metadata", function () { var metadataClass = new MetadataClass({ id: "test", From 580d883ade46c5501314747f601a1d56625262a0 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 14:15:23 -0400 Subject: [PATCH 09/31] Adds B3dmLoaderSpec --- Source/Scene/ModelExperimental/B3dmLoader.js | 41 ++---- .../Scene/ModelExperimental/B3dmLoaderSpec.js | 131 ++++++++++++++++++ 2 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 Specs/Scene/ModelExperimental/B3dmLoaderSpec.js diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 97a2bcda68dd..f8c6eb5acb53 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -10,12 +10,12 @@ import FeatureTable from "../FeatureTable.js"; import FeatureMetadata from "../FeatureMetadata.js"; import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; import GltfLoader from "../GltfLoader.js"; +import Matrix4 from "../../Core/Matrix4.js"; +import MetadataClass from "../MetadataClass.js"; import parseBatchTable from "../parseBatchTable.js"; import ResourceLoader from "../ResourceLoader.js"; import RuntimeError from "../../Core/RuntimeError.js"; import when from "../../ThirdParty/when.js"; -import MetadataClass from "../MetadataClass.js"; -import Matrix4 from "../../Core/Matrix4.js"; var B3dmLoaderState = { UNLOADED: 0, @@ -83,16 +83,17 @@ function B3dmLoader(options) { this._upAxis = upAxis; this._forwardAxis = forwardAxis; - // Since the 3D Tiles code handles loading of the tile contents, the B3dmLoader starts at the LOADED stage. - this._state = B3dmLoaderState.LOADED; + this._state = B3dmLoaderState.UNLOADED; + this._promise = when.defer(); this._texturesLoadedPromise = when.defer(); + this._gltfLoader = undefined; // Loaded results. this._batchLength = 0; - this._rtcTransform = undefined; this._featureTable = undefined; + // The batch table object contains a json and a binary component access using keys of the same name. this._batchTable = undefined; this._components = undefined; } @@ -162,20 +163,6 @@ Object.defineProperties(B3dmLoader.prototype, { return this._components; }, }, - /** - * The RTC transform stored in the feature table. - * - * @memberof B3dmLoader.prototype - * - * @type {Matrix4} - * @readonly - * @private - */ - rtcTransform: { - get: function () { - return this._rtcTransform; - }, - }, }); /** @@ -384,11 +371,6 @@ function loadBatchTable( var batchTableJson; var batchTableBinary; if (batchTableJsonByteLength > 0) { - // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the - // arraybuffer/string compressed in memory and then decompress it when it is first accessed. - // - // We could also make another request for it, but that would make the property set/get - // API async, and would double the number of numbers in some cases. batchTableJson = getJsonFromTypedArray( typedArray, offset, @@ -420,6 +402,7 @@ B3dmLoader.prototype.process = function (frameState) { Check.typeOf.object("frameState", frameState); //>>includeEnd('debug'); + this._state = B3dmLoaderState.PROCESSING; var gltfLoader = this._gltfLoader; // Once the components are loaded, add the feature metadata created from the batch table. @@ -430,11 +413,13 @@ B3dmLoader.prototype.process = function (frameState) { return; } - var components = gltfLoader.components; - processGltfComponents(that, components); - that._components = components; + if (that._state !== B3dmLoaderState.READY) { + var components = gltfLoader.components; + processGltfComponents(that, components); + that._components = components; + that._state = B3dmLoaderState.READY; + } - that._state = B3dmLoader.READY; that._promise.resolve(that); }); diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js new file mode 100644 index 000000000000..6ac503e57788 --- /dev/null +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -0,0 +1,131 @@ +import { + B3dmLoader, + Cartesian3, + MetadataClass, + Resource, + ResourceCache, +} from "../../../Source/Cesium.js"; +import createScene from "../../createScene.js"; +import waitForLoaderProcess from "../../waitForLoaderProcess.js"; +describe("Scene/ModelExperimental/B3dmLoader", function () { + var withBatchTableUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithBatchTable/batchedWithBatchTable.b3dm"; + var withBatchTableBinaryUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithBatchTableBinary/batchedWithBatchTableBinary.b3dm"; + var withoutBatchTableUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/batchedWithoutBatchTable.b3dm"; + var withRtcCenterUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithRtcCenter/batchedWithRtcCenter.b3dm"; + var withBatchTableHierarchy = + "./Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm"; + + var scene; + var b3dmLoaders = []; + + beforeAll(function () { + scene = createScene(); + }); + + afterAll(function () { + scene.destroyForSpecs(); + }); + + afterEach(function () { + for (var i = 0; i < b3dmLoaders.length; i++) { + var loader = b3dmLoaders[i]; + if (!loader.isDestroyed()) { + loader.destroy(); + } + } + b3dmLoaders.length = 0; + ResourceCache.clearForSpecs(); + }); + + function loadB3dm(b3dmPath, options) { + var resource = Resource.createIfNeeded(b3dmPath); + + return Resource.fetchArrayBuffer({ + url: b3dmPath, + }).then(function (arrayBuffer) { + var loader = new B3dmLoader({ + b3dmResource: resource, + arrayBuffer: arrayBuffer, + }); + b3dmLoaders.push(loader); + loader.load(); + + return waitForLoaderProcess(loader, scene); + }); + } + + it("loads BatchedWithBatchTable", function () { + return loadB3dm(withBatchTableUrl).then(function (loader) { + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + expect(featureTable.class).toBeDefined(); + }); + }); + + it("loads BatchedWithBatchTableBinary", function () { + return loadB3dm(withBatchTableBinaryUrl).then(function (loader) { + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + expect(featureTable.class).toBeDefined(); + }); + }); + + it("loads BatchedWithoutBatchTableUrl", function () { + return loadB3dm(withoutBatchTableUrl).then(function (loader) { + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + expect(featureTable.class).toBeUndefined(); + }); + }); + + it("loads BatchedWithRtcCenterUrl", function () { + return loadB3dm(withRtcCenterUrl).then(function (loader) { + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + var rootNodeMatrix = components.scene.nodes[0].matrix; + var translation = new Cartesian3( + rootNodeMatrix[12], + rootNodeMatrix[13], + rootNodeMatrix[14] + ); + expect(translation).toEqual(new Cartesian3(0.1, 0.3, -0.2)); + }); + }); + + it("loads BatchTableHierarchy", function () { + return loadB3dm(withBatchTableHierarchy).then(function (loader) { + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(30); + expect(featureTable._batchTableHierarchy).toBeDefined(); + }); + }); +}); From 1b5f69577e0ebebd0a42fa4e687b4bd55999d644 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 14:50:31 -0400 Subject: [PATCH 10/31] Fixes pipeline specs --- .../FeatureIdPipelineStage.js | 8 +++-- .../ModelExperimental/buildDrawCommand.js | 2 +- .../BatchTexturePipelineStageSpec.js | 32 +------------------ .../ModelExperimentalPrimitiveSpec.js | 11 +++++-- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js index ff1ed9c85060..9733de139ed8 100644 --- a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js +++ b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js @@ -98,17 +98,21 @@ function getFeatureIdAttributeInfo( function processFeatureIdAttributes(renderResources, frameState, primitive) { var shaderBuilder = renderResources.shaderBuilder; var model = renderResources.model; + var instances = renderResources.runtimeNode.node.instances; var featureIdAttributePrefix; var featureIdAttributeSetIndex; // For 3D Tiles 1.0, the FEATURE_ID vertex attribute is present but the Feature ID attribute is not. - if (primitive.featureIdAttributes.length === 0) { + if ( + primitive.featureIdAttributes.length === 0 && + defined(instances) && + instances.featureIdAttributes.length === 0 + ) { featureIdAttributePrefix = "a_featureId"; featureIdAttributeSetIndex = ""; } else { var featureIdAttributeIndex = model.featureIdAttributeIndex; - var instances = renderResources.runtimeNode.node.instances; var featureIdAttributeInfo = getFeatureIdAttributeInfo( featureIdAttributeIndex, diff --git a/Source/Scene/ModelExperimental/buildDrawCommand.js b/Source/Scene/ModelExperimental/buildDrawCommand.js index 0cc0621c3992..afdabd5ed07d 100644 --- a/Source/Scene/ModelExperimental/buildDrawCommand.js +++ b/Source/Scene/ModelExperimental/buildDrawCommand.js @@ -53,6 +53,6 @@ export default function buildDrawCommand(primitiveRenderResources, frameState) { pickId: primitiveRenderResources.pickId, instanceCount: primitiveRenderResources.instanceCount, primitiveType: primitiveRenderResources.primitiveType, - debugShowBoundingVolume: true, + debugShowBoundingVolume: model.debugShowBoundingVolume, }); } diff --git a/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js b/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js index 08579b5b5e4d..f53313951ea4 100644 --- a/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js @@ -54,8 +54,8 @@ describe("Scene/ModelExperimental/BatchTexturePipelineStage", function () { it("sets up batch textures from ModelExperimental", function () { var renderResources = { shaderBuilder: new ShaderBuilder(), - featureTableId: "mockFeatureTable", model: { + featureTableId: "mockFeatureTable", featureTables: { mockFeatureTable: { featuresLength: 10, @@ -78,34 +78,4 @@ describe("Scene/ModelExperimental/BatchTexturePipelineStage", function () { renderResources.uniformMap ); }); - - it("sets up batch textures from Cesium3DTileContent", function () { - var renderResources = { - shaderBuilder: new ShaderBuilder(), - featureTableId: "mockFeatureTable", - model: { - content: { - featureTables: { - mockFeatureTable: { - featuresLength: 10, - batchTexture: { - batchTexture: 0, - textureDimensions: { - y: 2, - }, - textureStep: 2, - }, - }, - }, - }, - }, - }; - - BatchTexturePipelineStage.process(renderResources, {}, {}); - verifyBatchTextureShaders(renderResources.shaderBuilder); - verifyBatchTextureUniforms( - renderResources.model.content.featureTables.mockFeatureTable, - renderResources.uniformMap - ); - }); }); diff --git a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js index 74addb95e34c..4428945cc376 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimentalPrimitiveSpec.js @@ -9,18 +9,19 @@ import { LightingPipelineStage, MaterialPipelineStage, PickingPipelineStage, + VertexAttributeSemantic, + BatchTexturePipelineStage, ModelExperimentalPrimitive, } from "../../../Source/Cesium.js"; -import BatchTexturePipelineStage from "../../../Source/Scene/ModelExperimental/BatchTexturePipelineStage.js"; describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { var mockPrimitive = { featureIdAttributes: [], featureIdTextures: [], + attributes: [], }; var mockNode = {}; var mockModel = { - content: {}, allowPicking: true, featureIdAttributeIndex: 0, }; @@ -144,6 +145,11 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: { featureIdAttributes: [{}, {}], featureIdTextures: [], + attributes: [ + { + semantic: VertexAttributeSemantic.FEATURE_ID, + }, + ], }, node: {}, model: { @@ -169,6 +175,7 @@ describe("Scene/ModelExperimental/ModelExperimentalPrimitive", function () { primitive: { featureIdAttributes: [], featureIdTextures: [{}, {}], + attributes: [], }, node: {}, model: { From f5552fc4fc0651870f50cdfd26ea06dc67a7db5b Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 14:52:59 -0400 Subject: [PATCH 11/31] Fixes ModelFeatureTable specs --- Source/Scene/ModelExperimental/ModelFeatureTable.js | 9 ++++++++- Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Scene/ModelExperimental/ModelFeatureTable.js b/Source/Scene/ModelExperimental/ModelFeatureTable.js index 8831c61f25f2..13b04521eac4 100644 --- a/Source/Scene/ModelExperimental/ModelFeatureTable.js +++ b/Source/Scene/ModelExperimental/ModelFeatureTable.js @@ -1,8 +1,9 @@ import BatchTexture from "../BatchTexture.js"; +import Cesium3DTileFeature from "../Cesium3DTileFeature.js"; +import Check from "../../Core/Check.js"; import defined from "../../Core/defined.js"; import destroyObject from "../../Core/destroyObject.js"; import ModelFeature from "./ModelFeature.js"; -import Cesium3DTileFeature from "../Cesium3DTileFeature.js"; /** * Manages the {@link ModelFeature}s in a {@link ModelExperimental}. @@ -21,6 +22,12 @@ import Cesium3DTileFeature from "../Cesium3DTileFeature.js"; export default function ModelFeatureTable(options) { this._featureTable = options.featureTable; this._model = options.model; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.featureTable", options.featureTable); + Check.typeOf.object("options.model", options.model); + //>>includeEnd('debug'); + this._features = undefined; this._featuresLength = 0; diff --git a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js index 3328f015b5ad..e80bae9b7deb 100644 --- a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js +++ b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js @@ -23,6 +23,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("hasProperty works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); var modelFeatures = table._features; for (var i = 0; i < modelFeatures.length; i++) { @@ -35,6 +36,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("getFeature works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); expect(table._featuresLength).toEqual(mockFeatureTable.count); var modelFeatures = table._features; @@ -48,6 +50,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("getProperty works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); expect(table._featuresLength).toEqual(mockFeatureTable.count); var modelFeatures = table._features; @@ -67,6 +70,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("getPropertyNames works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); var modelFeatures = table._features; var results; @@ -80,6 +84,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("setProperty works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); var feature = table._features[0]; expect(feature.getProperty("height")).toEqual(1.0); @@ -90,6 +95,7 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { it("destroy works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, + model: {}, }); var batchTexture = table._batchTexture; expect(batchTexture.isDestroyed()).toEqual(false); From e90073e153455b2a8df1ba68f7bd03901f4755dd Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 14:59:22 -0400 Subject: [PATCH 12/31] Adds specs to check if the right type of features are created in ModelFeatureTable --- .../ModelFeatureTableSpec.js | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js index e80bae9b7deb..8a07096feeb6 100644 --- a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js +++ b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js @@ -1,4 +1,8 @@ -import { ModelFeatureTable, ModelFeature } from "../../../Source/Cesium.js"; +import { + Cesium3DTileFeature, + ModelFeatureTable, + ModelFeature, +} from "../../../Source/Cesium.js"; import MetadataTester from "../../MetadataTester.js"; describe("Scene/ModelExperimental/ModelFeatureTable", function () { @@ -20,6 +24,34 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { propertyValues: propertyValues, }); + it("creates ModelFeatures when model does not have content", function () { + var table = new ModelFeatureTable({ + featureTable: mockFeatureTable, + model: {}, + }); + expect(table._featuresLength).toEqual(mockFeatureTable.count); + var modelFeatures = table._features; + for (var i = 0; i < modelFeatures.length; i++) { + var feature = table.getFeature(i); + expect(feature).toBeInstanceOf(ModelFeature); + } + }); + + it("creates ModelFeatures when model has content", function () { + var table = new ModelFeatureTable({ + featureTable: mockFeatureTable, + model: { + content: {}, + }, + }); + expect(table._featuresLength).toEqual(mockFeatureTable.count); + var modelFeatures = table._features; + for (var i = 0; i < modelFeatures.length; i++) { + var feature = table.getFeature(i); + expect(feature).toBeInstanceOf(Cesium3DTileFeature); + } + }); + it("hasProperty works", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, From b2d2065b2200fb65a2e7d18fdd719210b350c7d2 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 14 Oct 2021 15:18:40 -0400 Subject: [PATCH 13/31] Spec fix --- Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js index 8a07096feeb6..df5e7ce49a00 100644 --- a/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js +++ b/Specs/Scene/ModelExperimental/ModelFeatureTableSpec.js @@ -41,7 +41,9 @@ describe("Scene/ModelExperimental/ModelFeatureTable", function () { var table = new ModelFeatureTable({ featureTable: mockFeatureTable, model: { - content: {}, + content: { + tileset: {}, + }, }, }); expect(table._featuresLength).toEqual(mockFeatureTable.count); From bd66720090eabbe9073a840b8b582d392be43b98 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Fri, 15 Oct 2021 14:08:58 -0400 Subject: [PATCH 14/31] Adds more coverage --- Source/Scene/Gltf3DTileContent.js | 5 +- .../FeatureIdPipelineStage.js | 6 +- .../ModelExperimental/ModelExperimental.js | 4 +- .../ModelExperimental3DTileContent.js | 20 ++- .../ModelExperimental/ModelFeatureTable.js | 32 +++- .../Scene/ModelExperimental/B3dmLoaderSpec.js | 81 +++++++++- .../ModelExperimental3DTileContentSpec.js | 148 ++++++++++++++++++ 7 files changed, 276 insertions(+), 20 deletions(-) create mode 100644 Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js diff --git a/Source/Scene/Gltf3DTileContent.js b/Source/Scene/Gltf3DTileContent.js index 360a4f56310f..e0378cba4bdb 100644 --- a/Source/Scene/Gltf3DTileContent.js +++ b/Source/Scene/Gltf3DTileContent.js @@ -8,10 +8,7 @@ import Model from "./Model.js"; import ModelAnimationLoop from "./ModelAnimationLoop.js"; /** - * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf/0.0.0|3DTILES_content_gltf} extension. - *

- * This class does not yet support the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/1.0.0|EXT_feature_metadata Extension}. - *

+ * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf/0.0.0|3DTILES_content_gltf} extension. *

* Implements the {@link Cesium3DTileContent} interface. *

diff --git a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js index 9733de139ed8..db161365303f 100644 --- a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js +++ b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js @@ -104,11 +104,7 @@ function processFeatureIdAttributes(renderResources, frameState, primitive) { var featureIdAttributeSetIndex; // For 3D Tiles 1.0, the FEATURE_ID vertex attribute is present but the Feature ID attribute is not. - if ( - primitive.featureIdAttributes.length === 0 && - defined(instances) && - instances.featureIdAttributes.length === 0 - ) { + if (primitive.featureIdAttributes.length === 0 && !defined(instances)) { featureIdAttributePrefix = "a_featureId"; featureIdAttributeSetIndex = ""; } else { diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 3b6df9c83d04..92e2a4a429d2 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -119,10 +119,10 @@ function createModelFeatureTables(model, featureMetadata) { } function selectFeatureTableId(components, model) { - var content = model._content; + var featureTables = model._featureTables; // For 3D Tiles 1.0 formats, the feature table always has the "_batchTable" feature table. - if (defined(content)) { + if (defined(featureTables[MetadataClass.BATCH_TABLE_CLASS_NAME])) { return MetadataClass.BATCH_TABLE_CLASS_NAME; } diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index defa01fdd5df..c4a75926adba 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -33,8 +33,14 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { featuresLength: { get: function () { var model = this._model; - var featureTable = model.featureTables[model.featureTableId]; - return featureTable.featuresLength; + var featureTables = model.featureTables; + var featureTableId = model.featureTableId; + + if (defined(featureTables) && defined(featureTables[featureTableId])) { + return featureTables[featureTableId].featuresLength; + } + + return 0; }, }, @@ -101,8 +107,14 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { batchTable: { get: function () { var model = this._model; - var featureTable = model.featureTables[model.featureTableId]; - return featureTable; + var featureTables = model.featureTables; + var featureTableId = model.featureTableId; + + if (defined(featureTables) && defined(featureTables[featureTableId])) { + return featureTables[featureTableId]; + } + + return undefined; }, }, diff --git a/Source/Scene/ModelExperimental/ModelFeatureTable.js b/Source/Scene/ModelExperimental/ModelFeatureTable.js index 13b04521eac4..fbc35394644d 100644 --- a/Source/Scene/ModelExperimental/ModelFeatureTable.js +++ b/Source/Scene/ModelExperimental/ModelFeatureTable.js @@ -115,6 +115,34 @@ ModelFeatureTable.prototype.update = function (frameState) { this._batchTexture.update(undefined, frameState); }; +ModelFeatureTable.prototype.setShow = function (featureId, show) { + this._batchTexture.setShow(featureId, show); +}; + +ModelFeatureTable.prototype.setAllShow = function (show) { + this._batchTexture.setAllShow(show); +}; + +ModelFeatureTable.prototype.getShow = function (featureId) { + return this._batchTexture.getShow(featureId); +}; + +ModelFeatureTable.prototype.setColor = function (featureId, color) { + this._batchTexture.setColor(featureId, color); +}; + +ModelFeatureTable.prototype.setAllColor = function (color) { + this._batchTexture.setAllColor(color); +}; + +ModelFeatureTable.prototype.getColor = function (featureId, result) { + return this._batchTexture.getColor(featureId, result); +}; + +ModelFeatureTable.prototype.getPickColor = function (featureId) { + return this._batchTexture.getPickColor(featureId); +}; + ModelFeatureTable.prototype.getFeature = function (featureId) { return this._features[featureId]; }; @@ -127,10 +155,6 @@ ModelFeatureTable.prototype.getProperty = function (featureId, name) { return this._featureTable.getProperty(featureId, name); }; -ModelFeatureTable.prototype.getPropertyInherited = function (featureId, name) { - return this._featureTable.getProperty(featureId, name); -}; - ModelFeatureTable.prototype.getPropertyNames = function (results) { return this._featureTable.getPropertyIds(results); }; diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 6ac503e57788..52685730c476 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -1,10 +1,12 @@ import { B3dmLoader, Cartesian3, + GltfLoader, MetadataClass, Resource, ResourceCache, } from "../../../Source/Cesium.js"; +import Cesium3DTilesTester from "../../Cesium3DTilesTester.js"; import createScene from "../../createScene.js"; import waitForLoaderProcess from "../../waitForLoaderProcess.js"; describe("Scene/ModelExperimental/B3dmLoader", function () { @@ -18,12 +20,18 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { "./Data/Cesium3DTiles/Batched/BatchedWithRtcCenter/batchedWithRtcCenter.b3dm"; var withBatchTableHierarchy = "./Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm"; + var deprecated1Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated1/batchedDeprecated1.b3dm"; + var deprecated2Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated2/batchedDeprecated2.b3dm"; var scene; var b3dmLoaders = []; beforeAll(function () { scene = createScene(); + // Keep the error from logging to the console when running tests + spyOn(B3dmLoader, "_deprecationWarning"); }); afterAll(function () { @@ -41,7 +49,18 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { ResourceCache.clearForSpecs(); }); - function loadB3dm(b3dmPath, options) { + // function loadB3dmFromArrayBuffer(arrayBuffer) { + // var loader = new B3dmLoader({ + // b3dmResource: Resource.createIfNeeded(""), + // arrayBuffer: arrayBuffer + // }); + // b3dmLoaders.push(loader); + // loader.load(); + + // return waitForLoaderProcess(loader, scene); + // } + + function loadB3dm(b3dmPath) { var resource = Resource.createIfNeeded(b3dmPath); return Resource.fetchArrayBuffer({ @@ -128,4 +147,64 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { expect(featureTable._batchTableHierarchy).toBeDefined(); }); }); + + 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 loadB3dm(deprecated1Url).then(function (loader) { + expect(B3dmLoader._deprecationWarning).toHaveBeenCalled(); + + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + }); + }); + + it("recognizes the legacy 24-byte header", function () { + return loadB3dm(deprecated2Url).then(function (loader) { + expect(B3dmLoader._deprecationWarning).toHaveBeenCalled(); + + var components = loader.components; + var featureMetadata = components.featureMetadata; + var featureTable = featureMetadata.getFeatureTable( + MetadataClass.BATCH_TABLE_CLASS_NAME + ); + expect(featureTable).toBeDefined(); + expect(featureTable.count).toEqual(10); + }); + }); + + 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("destroys B3DM loader", function () { + var unloadGltfLoader = spyOn( + GltfLoader.prototype, + "unload" + ).and.callThrough(); + + return loadB3dm(withBatchTableUrl).then(function (loader) { + expect(loader.components).toBeDefined(); + expect(loader.isDestroyed()).toBe(false); + + loader.destroy(); + + expect(loader.components).toBeUndefined(); + expect(loader.isDestroyed()).toBe(true); + + expect(unloadGltfLoader.calls.count()).toBe(1); + }); + }); }); diff --git a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js new file mode 100644 index 000000000000..b3453523e49f --- /dev/null +++ b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js @@ -0,0 +1,148 @@ +import { + Cartesian3, + defined, + ExperimentalFeatures, + HeadingPitchRange, +} from "../../../Source/Cesium.js"; +import Cesium3DTilesTester from "../../Cesium3DTilesTester.js"; +import createScene from "../../createScene.js"; + +describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function () { + var gltfContentUrl = "./Data/Cesium3DTiles/GltfContent/glTF/tileset.json"; + var glbContentUrl = "./Data/Cesium3DTiles/GltfContent/glb/tileset.json"; + var buildingsMetadataUrl = + "./Data/Cesium3DTiles/Metadata/FeatureMetadata/tileset.json"; + var withBatchTableUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json"; + + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + function setCamera(longitude, latitude, height) { + // 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, defined(height) ? height : 100.0) + ); + } + + beforeAll(function () { + ExperimentalFeatures.enableModelExperimental = true; + scene = createScene(); + }); + + afterAll(function () { + ExperimentalFeatures.enableModelExperimental = false; + scene.destroyForSpecs(); + }); + + beforeEach(function () { + setCamera(centerLongitude, centerLatitude); + }); + + afterEach(function () { + scene.primitives.removeAll(); + }); + + it("resolves readyPromise with glb", function () { + return Cesium3DTilesTester.resolvesReadyPromise(scene, glbContentUrl); + }); + + it("resolves readyPromise with glTF", function () { + return Cesium3DTilesTester.resolvesReadyPromise(scene, gltfContentUrl); + }); + + it("resolves readyPromise with B3DM", function () { + setCamera(centerLongitude, centerLatitude, 15.0); + return Cesium3DTilesTester.resolvesReadyPromise(scene, withBatchTableUrl); + }); + + it("renders glTF content", function () { + return Cesium3DTilesTester.loadTileset(scene, buildingsMetadataUrl).then( + function (tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + } + ); + }); + + it("renders B3DM content", function () { + setCamera(centerLongitude, centerLatitude, 15.0); + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( + function (tileset) { + Cesium3DTilesTester.expectRender(scene, tileset); + } + ); + }); + + it("picks from glTF", function () { + return Cesium3DTilesTester.loadTileset(scene, gltfContentUrl).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 from B3DM", function () { + setCamera(centerLongitude, centerLatitude, 15.0); + 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 from glTF feature table", function () { + return Cesium3DTilesTester.loadTileset(scene, buildingsMetadataUrl).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); + expect(content.hasProperty(0, "id")).toBe(true); + expect(content.getFeature(0)).toBeDefined(); + }); + } + ); + }); + + it("picks from B3DM batch table", function () { + setCamera(centerLongitude, centerLatitude, 15.0); + 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); + expect(content.hasProperty(0, "id")).toBe(true); + expect(content.getFeature(0)).toBeDefined(); + }); + } + ); + }); +}); From 38c3b14cfc1a409c4dc86bb89fc2116924358744 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Fri, 15 Oct 2021 14:32:24 -0400 Subject: [PATCH 15/31] More specs --- .../ModelExperimental3DTileContentSpec.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js index b3453523e49f..1dc51a528206 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js @@ -2,7 +2,9 @@ import { Cartesian3, defined, ExperimentalFeatures, + GroupMetadata, HeadingPitchRange, + MetadataClass, } from "../../../Source/Cesium.js"; import Cesium3DTilesTester from "../../Cesium3DTilesTester.js"; import createScene from "../../createScene.js"; @@ -14,6 +16,8 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function "./Data/Cesium3DTiles/Metadata/FeatureMetadata/tileset.json"; var withBatchTableUrl = "./Data/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json"; + var withoutBatchTableUrl = + "./Data/Cesium3DTiles/Batched/BatchedWithoutBatchTable/tileset.json"; var scene; var centerLongitude = -1.31968; @@ -145,4 +149,45 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function } ); }); + + it("destroys", function () { + return Cesium3DTilesTester.tileDestroys(scene, buildingsMetadataUrl); + }); + + describe("3DTILES_metadata", function () { + var metadataClass = new MetadataClass({ + id: "test", + class: { + properties: { + name: { + type: "STRING", + }, + height: { + type: "FLOAT32", + }, + }, + }, + }); + var groupMetadata = new GroupMetadata({ + id: "testGroup", + group: { + properties: { + name: "Test Group", + height: 35.6, + }, + }, + class: metadataClass, + }); + + it("assigns groupMetadata", function () { + setCamera(centerLongitude, centerLatitude, 15.0); + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then( + function (tileset) { + var content = tileset.root.content; + content.groupMetadata = groupMetadata; + expect(content.groupMetadata).toBe(groupMetadata); + } + ); + }); + }); }); From 2bc458646fd320e2d8a235342974fe3f7a2233a5 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Fri, 15 Oct 2021 15:06:24 -0400 Subject: [PATCH 16/31] Adds more spec for the right type of picking in all cases --- .../ModelExperimental3DTileContent.js | 14 ++++++-------- .../ModelExperimental3DTileContentSpec.js | 6 +++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index c4a75926adba..3c37a593d6d8 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -130,13 +130,12 @@ Object.defineProperties(ModelExperimental3DTileContent.prototype, { ModelExperimental3DTileContent.prototype.getFeature = function (featureId) { var model = this._model; - var featureTables = model.featureTables; var featureTableId = model.featureTableId; - var featureTable = featureTables[featureTableId]; - - if (!defined(featureTable)) { + if (!defined(featureTableId)) { return undefined; } + + var featureTable = model.featureTables[featureTableId]; return featureTable.getFeature(featureId); }; @@ -145,13 +144,12 @@ ModelExperimental3DTileContent.prototype.hasProperty = function ( name ) { var model = this._model; - var featureTables = model.featureTables; var featureTableId = model.featureTableId; - var featureTable = featureTables[featureTableId]; - - if (!defined(featureTable)) { + if (!defined(featureTableId)) { return false; } + + var featureTable = model.featureTables[featureTableId]; return featureTable.hasProperty(featureId, name); }; diff --git a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js index 1dc51a528206..7985e01f06d4 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js @@ -91,6 +91,8 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); + expect(content.hasProperty(0, "id")).toBe(false); + expect(content.getFeature(0)).toBeUndefined(); }); } ); @@ -98,7 +100,7 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function it("picks from B3DM", function () { setCamera(centerLongitude, centerLatitude, 15.0); - return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then( + return Cesium3DTilesTester.loadTileset(scene, withoutBatchTableUrl).then( function (tileset) { var content = tileset.root.content; tileset.show = false; @@ -108,6 +110,8 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); + expect(content.hasProperty(0, "id")).toBe(false); + expect(content.getFeature(0)).toBeDefined(); }); } ); From 5404f847c1a44b6802c70c148dda6bd900a2c496 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 12:19:41 -0400 Subject: [PATCH 17/31] Adds RTC Transform object to loader --- Source/Scene/ModelExperimental/B3dmLoader.js | 55 ++++++++++--------- .../ModelExperimental/ModelExperimental.js | 5 ++ .../Scene/ModelExperimental/B3dmLoaderSpec.js | 22 ++------ 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index f8c6eb5acb53..51e4c8d606c9 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -96,6 +96,7 @@ function B3dmLoader(options) { // The batch table object contains a json and a binary component access using keys of the same name. this._batchTable = undefined; this._components = undefined; + this._rtcTransform = undefined; } if (defined(Object.create)) { @@ -149,6 +150,7 @@ Object.defineProperties(B3dmLoader.prototype, { return undefined; }, }, + /** * The loaded components. * @@ -163,6 +165,22 @@ Object.defineProperties(B3dmLoader.prototype, { return this._components; }, }, + + /** + * A transform from the RTC_CENTER semantic, if present in the B3DM's Feature Table. + * See {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel#global-semantics} + * + * @memberof B3dmLoader.prototype + * + * @type {Matrix4} + * @readonly + * @private + */ + rtcTransform: { + get: function () { + return this._rtcTransform; + }, + }, }); /** @@ -199,6 +217,18 @@ B3dmLoader.prototype.load = function () { offset += featureTableJsonByteLength + featureTableBinaryByteLength; this._featureTable = featureTable; + // Set the RTC Center transform, if present. + var rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3 + ); + if (defined(rtcCenter)) { + this._rtcTransform = Matrix4.fromTranslation( + Cartesian3.fromArray(rtcCenter) + ); + } + // Set batch length. batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); featureTable.featuresLength = batchLength; @@ -442,7 +472,6 @@ B3dmLoader.prototype.process = function (frameState) { function processGltfComponents(loader, components) { var batchTable = loader._batchTable; var batchLength = loader._batchLength; - var featureTable = loader._featureTable; var featureMetadata; if (defined(batchTable.json)) { @@ -465,30 +494,6 @@ function processGltfComponents(loader, components) { }); } components.featureMetadata = featureMetadata; - - // Apply the RTC Center transform, if present. - var rtcCenter = featureTable.getGlobalProperty( - "RTC_CENTER", - ComponentDatatype.FLOAT, - 3 - ); - if (defined(rtcCenter)) { - var rootNodes = components.scene.nodes; - for (var i = 0; i < rootNodes.length; i++) { - applyRTCTransform(rootNodes[i], rtcCenter); - } - } -} - -function applyRTCTransform(node, rtcCenter) { - if (defined(node.matrix)) { - var rtcTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); - Matrix4.multiply(node.matrix, rtcTransform, node.matrix); - } else if (defined(node.translation)) { - Cartesian3.add(rtcCenter, node.translation, node.translation); - } else { - node.translation = rtcCenter; - } } B3dmLoader.prototype.unload = function () { diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 92e2a4a429d2..284529e2b52b 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -175,6 +175,11 @@ function initialize(model) { loader.promise .then(function (loader) { + var rtcTransform = loader.rtcTransform; + if (defined(rtcTransform)) { + Matrix4.multiply(modelMatrix, rtcTransform, modelMatrix); + } + var components = loader.components; var content = model._content; var featureMetadata = components.featureMetadata; diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 52685730c476..8404b02369ec 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -2,6 +2,7 @@ import { B3dmLoader, Cartesian3, GltfLoader, + Matrix4, MetadataClass, Resource, ResourceCache, @@ -9,6 +10,7 @@ import { import Cesium3DTilesTester from "../../Cesium3DTilesTester.js"; import createScene from "../../createScene.js"; import waitForLoaderProcess from "../../waitForLoaderProcess.js"; + describe("Scene/ModelExperimental/B3dmLoader", function () { var withBatchTableUrl = "./Data/Cesium3DTiles/Batched/BatchedWithBatchTable/batchedWithBatchTable.b3dm"; @@ -49,17 +51,6 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { ResourceCache.clearForSpecs(); }); - // function loadB3dmFromArrayBuffer(arrayBuffer) { - // var loader = new B3dmLoader({ - // b3dmResource: Resource.createIfNeeded(""), - // arrayBuffer: arrayBuffer - // }); - // b3dmLoaders.push(loader); - // loader.load(); - - // return waitForLoaderProcess(loader, scene); - // } - function loadB3dm(b3dmPath) { var resource = Resource.createIfNeeded(b3dmPath); @@ -125,13 +116,10 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { ); expect(featureTable).toBeDefined(); expect(featureTable.count).toEqual(10); - var rootNodeMatrix = components.scene.nodes[0].matrix; - var translation = new Cartesian3( - rootNodeMatrix[12], - rootNodeMatrix[13], - rootNodeMatrix[14] + + expect(loader.rtcTransform).toEqual( + Matrix4.fromTranslation(new Cartesian3(0.1, 0.2, 0.3)) ); - expect(translation).toEqual(new Cartesian3(0.1, 0.3, -0.2)); }); }); From 1d51698230799d6d056f71450343ffe7bc418f40 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 13:36:13 -0400 Subject: [PATCH 18/31] Adds B3dmParser --- Source/Scene/B3dmParser.js | 176 ++++++++++++++ Source/Scene/Batched3DModel3DTileContent.js | 143 +---------- Source/Scene/ModelExperimental/B3dmLoader.js | 230 ++---------------- Specs/Scene/B3dmParserSpec.js | 0 .../Scene/Batched3DModel3DTileContentSpec.js | 16 +- .../Scene/ModelExperimental/B3dmLoaderSpec.js | 7 +- 6 files changed, 215 insertions(+), 357 deletions(-) create mode 100644 Source/Scene/B3dmParser.js create mode 100644 Specs/Scene/B3dmParserSpec.js diff --git a/Source/Scene/B3dmParser.js b/Source/Scene/B3dmParser.js new file mode 100644 index 000000000000..530927ca9671 --- /dev/null +++ b/Source/Scene/B3dmParser.js @@ -0,0 +1,176 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import deprecationWarning from "../Core/deprecationWarning.js"; +import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js"; +import RuntimeError from "../Core/RuntimeError.js"; + +/** + * Handles parsing of a Batched 3D Model. + * + * @namespace B3dmParser + * @private + */ +var B3dmParser = {}; +B3dmParser._deprecationWarning = deprecationWarning; + +var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; + +/** + * Parses the contents of a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel|Batched 3D Model}. + * + * @private + * + * @param {ArrayBuffer} arrayBuffer The array buffer containing the B3DM. + * @param {Number} [byteOffset=0] The byte offset of the beginning of the B3DM in the array buffer. + * @returns {Object} Returns an object with the batch length, feature table (binary and json), batch table (binary and json) and glTF parts of the B3DM. + */ +B3dmParser.parse = function (arrayBuffer, byteOffset) { + //>>includeStart('debug', pragmas.debug); + Check.defined("arrayBuffer", arrayBuffer); + //>>includeEnd('debug'); + + var byteStart = defaultValue(byteOffset, 0); + byteOffset = byteStart; + + var uint8Array = new Uint8Array(arrayBuffer); + var view = new DataView(arrayBuffer); + byteOffset += sizeOfUint32; // Skip magic + + var version = view.getUint32(byteOffset, true); + if (version !== 1) { + throw new RuntimeError( + "Only Batched 3D Model version 1 is supported. Version " + + version + + " is not." + ); + } + byteOffset += sizeOfUint32; + + var byteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var featureTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableJsonByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchTableBinaryByteLength = view.getUint32(byteOffset, true); + byteOffset += sizeOfUint32; + + var batchLength; + + // Legacy header #1: [batchLength] [batchTableByteLength] + // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] + // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] + // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. + // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. + // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead + if (batchTableJsonByteLength >= 570425344) { + // First legacy check + byteOffset -= sizeOfUint32 * 2; + batchLength = featureTableJsonByteLength; + batchTableJsonByteLength = featureTableBinaryByteLength; + batchTableBinaryByteLength = 0; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + B3dmParser._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } else if (batchTableBinaryByteLength >= 570425344) { + // Second legacy check + byteOffset -= sizeOfUint32; + batchLength = batchTableJsonByteLength; + batchTableJsonByteLength = featureTableJsonByteLength; + batchTableBinaryByteLength = featureTableBinaryByteLength; + featureTableJsonByteLength = 0; + featureTableBinaryByteLength = 0; + B3dmParser._deprecationWarning( + "b3dm-legacy-header", + "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." + ); + } + + var featureTableJson; + if (featureTableJsonByteLength === 0) { + featureTableJson = { + BATCH_LENGTH: defaultValue(batchLength, 0), + }; + } else { + featureTableJson = getJsonFromTypedArray( + uint8Array, + byteOffset, + featureTableJsonByteLength + ); + byteOffset += featureTableJsonByteLength; + } + + var featureTableBinary = new Uint8Array( + arrayBuffer, + byteOffset, + featureTableBinaryByteLength + ); + byteOffset += featureTableBinaryByteLength; + + var batchTableJson; + var batchTableBinary; + if (batchTableJsonByteLength > 0) { + // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the + // arraybuffer/string compressed in memory and then decompress it when it is first accessed. + // + // We could also make another request for it, but that would make the property set/get + // API async, and would double the number of numbers in some cases. + batchTableJson = getJsonFromTypedArray( + uint8Array, + byteOffset, + batchTableJsonByteLength + ); + byteOffset += batchTableJsonByteLength; + + if (batchTableBinaryByteLength > 0) { + // Has a batch table binary + batchTableBinary = new Uint8Array( + arrayBuffer, + byteOffset, + batchTableBinaryByteLength + ); + // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed + batchTableBinary = new Uint8Array(batchTableBinary); + byteOffset += batchTableBinaryByteLength; + } + } + + var gltfByteLength = byteStart + byteLength - byteOffset; + if (gltfByteLength === 0) { + throw new RuntimeError("glTF byte length must be greater than 0."); + } + + var gltfView; + if (byteOffset % 4 === 0) { + gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); + } else { + // Create a copy of the glb so that it is 4-byte aligned + B3dmParser._deprecationWarning( + "b3dm-glb-unaligned", + "The embedded glb is not aligned to a 4-byte boundary." + ); + gltfView = new Uint8Array( + uint8Array.subarray(byteOffset, byteOffset + gltfByteLength) + ); + } + + return { + batchLength: batchLength, + featureTableJson: featureTableJson, + featureTableBinary: featureTableBinary, + batchTableJson: batchTableJson, + batchTableBinary: batchTableBinary, + gltf: gltfView, + }; +}; + +export default B3dmParser; diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index 3bf8ef3db025..15b962067aec 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -1,17 +1,15 @@ import Cartesian3 from "../Core/Cartesian3.js"; import Color from "../Core/Color.js"; import ComponentDatatype from "../Core/ComponentDatatype.js"; -import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import deprecationWarning from "../Core/deprecationWarning.js"; import destroyObject from "../Core/destroyObject.js"; import DeveloperError from "../Core/DeveloperError.js"; -import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js"; import Matrix4 from "../Core/Matrix4.js"; import RequestType from "../Core/RequestType.js"; -import RuntimeError from "../Core/RuntimeError.js"; import Pass from "../Renderer/Pass.js"; import Axis from "./Axis.js"; +import B3dmParser from "./B3dmParser.js"; import Cesium3DTileBatchTable from "./Cesium3DTileBatchTable.js"; import Cesium3DTileFeature from "./Cesium3DTileFeature.js"; import Cesium3DTileFeatureTable from "./Cesium3DTileFeatureTable.js"; @@ -150,8 +148,6 @@ Object.defineProperties(Batched3DModel3DTileContent.prototype, { }, }); -var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; - function getBatchIdAttributeName(gltf) { var batchIdAttributeName = ModelUtility.getAttributeOrUniformBySemantic( gltf, @@ -239,93 +235,12 @@ function initialize(content, arrayBuffer, byteOffset) { var tile = content._tile; var resource = content._resource; - var byteStart = defaultValue(byteOffset, 0); - byteOffset = byteStart; - - var uint8Array = new Uint8Array(arrayBuffer); - var view = new DataView(arrayBuffer); - byteOffset += sizeOfUint32; // Skip magic - - var version = view.getUint32(byteOffset, true); - if (version !== 1) { - throw new RuntimeError( - "Only Batched 3D Model version 1 is supported. Version " + - version + - " is not." - ); - } - byteOffset += sizeOfUint32; - - var byteLength = view.getUint32(byteOffset, true); - byteOffset += sizeOfUint32; - - var featureTableJsonByteLength = view.getUint32(byteOffset, true); - byteOffset += sizeOfUint32; - - var featureTableBinaryByteLength = view.getUint32(byteOffset, true); - byteOffset += sizeOfUint32; - - var batchTableJsonByteLength = view.getUint32(byteOffset, true); - byteOffset += sizeOfUint32; - - var batchTableBinaryByteLength = view.getUint32(byteOffset, true); - byteOffset += sizeOfUint32; - - var batchLength; - - // Legacy header #1: [batchLength] [batchTableByteLength] - // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] - // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] - // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. - // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. - // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead - if (batchTableJsonByteLength >= 570425344) { - // First legacy check - byteOffset -= sizeOfUint32 * 2; - batchLength = featureTableJsonByteLength; - batchTableJsonByteLength = featureTableBinaryByteLength; - batchTableBinaryByteLength = 0; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - Batched3DModel3DTileContent._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } else if (batchTableBinaryByteLength >= 570425344) { - // Second legacy check - byteOffset -= sizeOfUint32; - batchLength = batchTableJsonByteLength; - batchTableJsonByteLength = featureTableJsonByteLength; - batchTableBinaryByteLength = featureTableBinaryByteLength; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - Batched3DModel3DTileContent._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } + var b3dm = B3dmParser.parse(arrayBuffer, byteOffset); - var featureTableJson; - if (featureTableJsonByteLength === 0) { - featureTableJson = { - BATCH_LENGTH: defaultValue(batchLength, 0), - }; - } else { - featureTableJson = getJsonFromTypedArray( - uint8Array, - byteOffset, - featureTableJsonByteLength - ); - byteOffset += featureTableJsonByteLength; - } - - var featureTableBinary = new Uint8Array( - arrayBuffer, - byteOffset, - featureTableBinaryByteLength - ); - byteOffset += featureTableBinaryByteLength; + var batchLength = b3dm.batchLength; + var featureTableJson = b3dm.featureTableJson; + var featureTableBinary = b3dm.featureTableBinary; var featureTable = new Cesium3DTileFeatureTable( featureTableJson, featureTableBinary @@ -334,33 +249,8 @@ function initialize(content, arrayBuffer, byteOffset) { batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); featureTable.featuresLength = batchLength; - var batchTableJson; - var batchTableBinary; - if (batchTableJsonByteLength > 0) { - // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the - // arraybuffer/string compressed in memory and then decompress it when it is first accessed. - // - // We could also make another request for it, but that would make the property set/get - // API async, and would double the number of numbers in some cases. - batchTableJson = getJsonFromTypedArray( - uint8Array, - byteOffset, - batchTableJsonByteLength - ); - byteOffset += batchTableJsonByteLength; - - if (batchTableBinaryByteLength > 0) { - // Has a batch table binary - batchTableBinary = new Uint8Array( - arrayBuffer, - byteOffset, - batchTableBinaryByteLength - ); - // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed - batchTableBinary = new Uint8Array(batchTableBinary); - byteOffset += batchTableBinaryByteLength; - } - } + var batchTableJson = b3dm.batchTableJson; + var batchTableBinary = b3dm.batchTableBinary; var colorChangedCallback; if (defined(content._classificationType)) { @@ -376,24 +266,7 @@ function initialize(content, arrayBuffer, byteOffset) { ); content._batchTable = batchTable; - var gltfByteLength = byteStart + byteLength - byteOffset; - if (gltfByteLength === 0) { - throw new RuntimeError("glTF byte length must be greater than 0."); - } - - var gltfView; - if (byteOffset % 4 === 0) { - gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); - } else { - // Create a copy of the glb so that it is 4-byte aligned - Batched3DModel3DTileContent._deprecationWarning( - "b3dm-glb-unaligned", - "The embedded glb is not aligned to a 4-byte boundary." - ); - gltfView = new Uint8Array( - uint8Array.subarray(byteOffset, byteOffset + gltfByteLength) - ); - } + var gltfView = b3dm.gltf; var pickObject = { content: content, diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 51e4c8d606c9..64593a252c39 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -1,4 +1,5 @@ import Axis from "../Axis.js"; +import B3dmParser from "../B3dmParser.js"; import Cartesian3 from "../../Core/Cartesian3.js"; import Cesium3DTileFeatureTable from "../Cesium3DTileFeatureTable.js"; import Check from "../../Core/Check.js"; @@ -8,13 +9,11 @@ import defined from "../../Core/defined.js"; import deprecationWarning from "../../Core/deprecationWarning.js"; import FeatureTable from "../FeatureTable.js"; import FeatureMetadata from "../FeatureMetadata.js"; -import getJsonFromTypedArray from "../../Core/getJsonFromTypedArray.js"; import GltfLoader from "../GltfLoader.js"; import Matrix4 from "../../Core/Matrix4.js"; import MetadataClass from "../MetadataClass.js"; import parseBatchTable from "../parseBatchTable.js"; import ResourceLoader from "../ResourceLoader.js"; -import RuntimeError from "../../Core/RuntimeError.js"; import when from "../../ThirdParty/when.js"; var B3dmLoaderState = { @@ -26,8 +25,6 @@ var B3dmLoaderState = { FAILED: 5, }; -var UINT32_BYTE_SIZE = Uint32Array.BYTES_PER_ELEMENT; - /** * Loads a Batched 3D Model. *

@@ -93,8 +90,9 @@ function B3dmLoader(options) { // Loaded results. this._batchLength = 0; this._featureTable = undefined; + // The batch table object contains a json and a binary component access using keys of the same name. - this._batchTable = undefined; + this._batc = undefined; this._components = undefined; this._rtcTransform = undefined; } @@ -188,35 +186,21 @@ Object.defineProperties(B3dmLoader.prototype, { * @private */ B3dmLoader.prototype.load = function () { - var byteStart = this._byteOffset; - var offset = byteStart; - var arrayBuffer = this._arrayBuffer; - var typedArray = new Uint8Array(arrayBuffer); - - // Parse B3DM header. - var header = parseHeader(arrayBuffer); - // The length of the B3DM header. - offset += header.headerByteLength; - // The byte length of the B3DM. - var byteLength = header.byteLength; - var batchLength = header.batchLength; - var batchTableJsonByteLength = header.batchTableJsonByteLength; - var batchTableBinaryByteLength = header.batchTableBinaryByteLength; - var featureTableJsonByteLength = header.featureTableJsonByteLength; - var featureTableBinaryByteLength = header.featureTableBinaryByteLength; - - // Load feature table. - var featureTable = loadFeatureTable( - arrayBuffer, - offset, - typedArray, - batchLength, - featureTableJsonByteLength, - featureTableBinaryByteLength - ); - offset += featureTableJsonByteLength + featureTableBinaryByteLength; - this._featureTable = featureTable; + var b3dm = B3dmParser.parse(this._arrayBuffer, this._offset); + + var batchLength = b3dm.batchLength; + var featureTableJson = b3dm.featureTableJson; + var featureTableBinary = b3dm.featureTableBinary; + var batchTableJson = b3dm.batchTableJson; + var batchTableBinary = b3dm.batchTableBinary; + var featureTable = new Cesium3DTileFeatureTable( + featureTableJson, + featureTableBinary + ); + batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); + // Set batch length. + this._batchLength = batchLength; // Set the RTC Center transform, if present. var rtcCenter = featureTable.getGlobalProperty( "RTC_CENTER", @@ -229,44 +213,14 @@ B3dmLoader.prototype.load = function () { ); } - // Set batch length. - batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); - featureTable.featuresLength = batchLength; - this._batchLength = batchLength; - - // Load batch table. - var batchTable = loadBatchTable( - arrayBuffer, - offset, - typedArray, - batchTableJsonByteLength, - batchTableBinaryByteLength - ); - offset += batchTableJsonByteLength + batchTableBinaryByteLength; - this._batchTable = batchTable; - - // Load glTF. - var gltfByteLength = byteStart + byteLength - offset; - if (gltfByteLength === 0) { - throw new RuntimeError("glTF byte length must be greater than 0."); - } - var gltfTypedArray; - if (offset % 4 === 0) { - gltfTypedArray = new Uint8Array(arrayBuffer, offset, gltfByteLength); - } else { - // Create a copy of the glb so that it is 4-byte aligned - B3dmLoader._deprecationWarning( - "b3dm-glb-unaligned", - "The embedded glb is not aligned to a 4-byte boundary." - ); - gltfTypedArray = new Uint8Array( - typedArray.subarray(offset, offset + gltfByteLength) - ); - } + this._batchTable = { + json: batchTableJson, + binary: batchTableBinary, + }; var gltfLoader = new GltfLoader({ upAxis: this._upAxis, - typedArray: gltfTypedArray, + typedArray: b3dm.gltf, forwardAxis: this._forwardAxis, gltfResource: this._b3dmResource, baseResource: this._baseResource, @@ -287,146 +241,6 @@ function handleError(gltfLoader, error) { error = gltfLoader.getError(errorMessage, error); gltfLoader._promise.reject(error); } - -function parseHeader(arrayBuffer) { - var offset = 0; - var dataView = new DataView(arrayBuffer); - - // Skip B3DM magic. - offset += UINT32_BYTE_SIZE; - var version = dataView.getUint32(offset, true); - if (version !== 1) { - throw new RuntimeError( - "Only Batched 3D Model version 1 is supported. Version " + - version + - " is not." - ); - } - offset += UINT32_BYTE_SIZE; - - var byteLength = dataView.getUint32(offset, true); - offset += UINT32_BYTE_SIZE; - var featureTableJsonByteLength = dataView.getUint32(offset, true); - offset += UINT32_BYTE_SIZE; - var featureTableBinaryByteLength = dataView.getUint32(offset, true); - offset += UINT32_BYTE_SIZE; - var batchTableJsonByteLength = dataView.getUint32(offset, true); - offset += UINT32_BYTE_SIZE; - var batchTableBinaryByteLength = dataView.getUint32(offset, true); - offset += UINT32_BYTE_SIZE; - - var batchLength; - // Legacy header #1: [batchLength] [batchTableByteLength] - // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] - // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] - // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. - // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. - // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead - if (batchTableJsonByteLength >= 570425344) { - // First legacy check - offset -= UINT32_BYTE_SIZE * 2; - batchLength = featureTableJsonByteLength; - batchTableJsonByteLength = featureTableBinaryByteLength; - batchTableBinaryByteLength = 0; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - B3dmLoader._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } else if (batchTableBinaryByteLength >= 570425344) { - // Second legacy check - offset -= UINT32_BYTE_SIZE; - batchLength = batchTableJsonByteLength; - batchTableJsonByteLength = featureTableJsonByteLength; - batchTableBinaryByteLength = featureTableBinaryByteLength; - featureTableJsonByteLength = 0; - featureTableBinaryByteLength = 0; - B3dmLoader._deprecationWarning( - "b3dm-legacy-header", - "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel." - ); - } - - return { - headerByteLength: offset, - byteLength: byteLength, - batchLength: batchLength, - batchTableJsonByteLength: batchTableJsonByteLength, - batchTableBinaryByteLength: batchTableBinaryByteLength, - featureTableJsonByteLength: featureTableJsonByteLength, - featureTableBinaryByteLength: featureTableBinaryByteLength, - }; -} - -function loadFeatureTable( - arrayBuffer, - offset, - typedArray, - batchLength, - featureTableJsonByteLength, - featureTableBinaryByteLength -) { - var featureTableJson; - if (featureTableJsonByteLength === 0) { - featureTableJson = { - BATCH_LENGTH: defaultValue(batchLength, 0), - }; - } else { - featureTableJson = getJsonFromTypedArray( - typedArray, - offset, - featureTableJsonByteLength - ); - offset += featureTableJsonByteLength; - } - - var featureTableBinary = new Uint8Array( - arrayBuffer, - offset, - featureTableBinaryByteLength - ); - offset += featureTableBinaryByteLength; - - return new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); -} - -function loadBatchTable( - arrayBuffer, - offset, - typedArray, - batchTableJsonByteLength, - batchTableBinaryByteLength -) { - var batchTableJson; - var batchTableBinary; - if (batchTableJsonByteLength > 0) { - batchTableJson = getJsonFromTypedArray( - typedArray, - offset, - batchTableJsonByteLength - ); - offset += batchTableJsonByteLength; - - if (batchTableBinaryByteLength > 0) { - // Has a batch table binary - batchTableBinary = new Uint8Array( - arrayBuffer, - offset, - batchTableBinaryByteLength - ); - // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed - batchTableBinary = new Uint8Array(batchTableBinary); - offset += batchTableBinaryByteLength; - } - } - - return { - json: batchTableJson, - binary: batchTableBinary, - }; -} - B3dmLoader.prototype.process = function (frameState) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("frameState", frameState); diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Specs/Scene/Batched3DModel3DTileContentSpec.js b/Specs/Scene/Batched3DModel3DTileContentSpec.js index 40648969e57a..cf68ea7fe653 100644 --- a/Specs/Scene/Batched3DModel3DTileContentSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentSpec.js @@ -1,11 +1,11 @@ import { + B3dmParser, Cartesian3, Color, HeadingPitchRange, HeadingPitchRoll, Matrix4, Transforms, - Batched3DModel3DTileContent, Cesium3DTilePass, ClippingPlane, ClippingPlaneCollection, @@ -58,7 +58,7 @@ describe( scene = createScene(); // Keep the error from logging to the console when running tests - spyOn(Batched3DModel3DTileContent, "_deprecationWarning"); + spyOn(B3dmParser, "_deprecationWarning"); }); afterAll(function () { @@ -83,9 +83,7 @@ describe( it("recognizes the legacy 20-byte header", function () { return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( function (tileset) { - expect( - Batched3DModel3DTileContent._deprecationWarning - ).toHaveBeenCalled(); + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); var batchTable = tileset.root.content.batchTable; expect(batchTable._properties).toBeDefined(); @@ -96,9 +94,7 @@ describe( it("recognizes the legacy 24-byte header", function () { return Cesium3DTilesTester.loadTileset(scene, deprecated2Url).then( function (tileset) { - expect( - Batched3DModel3DTileContent._deprecationWarning - ).toHaveBeenCalled(); + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); var batchTable = tileset.root.content.batchTable; expect(batchTable._properties).toBeDefined(); @@ -109,9 +105,7 @@ describe( it("logs deprecation warning for use of BATCHID without prefixed underscore", function () { return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( function (tileset) { - expect( - Batched3DModel3DTileContent._deprecationWarning - ).toHaveBeenCalled(); + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); Cesium3DTilesTester.expectRenderTileset(scene, tileset); } ); diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 8404b02369ec..9757212ef2e1 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -1,5 +1,6 @@ import { B3dmLoader, + B3dmParser, Cartesian3, GltfLoader, Matrix4, @@ -33,7 +34,7 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { beforeAll(function () { scene = createScene(); // Keep the error from logging to the console when running tests - spyOn(B3dmLoader, "_deprecationWarning"); + spyOn(B3dmParser, "_deprecationWarning"); }); afterAll(function () { @@ -145,7 +146,7 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { it("recognizes the legacy 20-byte header", function () { return loadB3dm(deprecated1Url).then(function (loader) { - expect(B3dmLoader._deprecationWarning).toHaveBeenCalled(); + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); var components = loader.components; var featureMetadata = components.featureMetadata; @@ -159,7 +160,7 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { it("recognizes the legacy 24-byte header", function () { return loadB3dm(deprecated2Url).then(function (loader) { - expect(B3dmLoader._deprecationWarning).toHaveBeenCalled(); + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); var components = loader.components; var featureMetadata = components.featureMetadata; From bd631377acde9fff0a402615eecabdee1b5b0fd3 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 13:38:44 -0400 Subject: [PATCH 19/31] Feedback pass --- Source/Scene/ModelExperimental/ModelExperimental.js | 3 +++ Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 284529e2b52b..567a593ed2a2 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -675,6 +675,9 @@ ModelExperimental.fromGltf = function (options) { return model; }; +/* + * @private + */ ModelExperimental.fromB3dm = function (options) { var loaderOptions = { b3dmResource: options.resource, diff --git a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js index 233e1c398a4c..2bd74663584c 100644 --- a/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js +++ b/Source/Scene/ModelExperimental/ModelExperimentalPrimitive.js @@ -111,7 +111,7 @@ function initialize(runtimePrimitive) { pipelineStages.push(LightingPipelineStage); - // The FeatureIdPipelineStage and BatchTexturePipelineStage when the primitive has features, i.e. when at least one of the following conditions exists: + // Add the FeatureIdPipelineStage and BatchTexturePipelineStage when the primitive has features, i.e. when at least one of the following conditions exists: // - the node is instanced and has feature ID attributes // - the primitive has a feature ID vertex attributes // - the primitive has a feature ID texture From 16a1eabc7eba64f36220c676d81d4393687eda83 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 15:27:03 -0400 Subject: [PATCH 20/31] Adds back B3dmParserSpec functions, some more checks --- Source/Scene/B3dmParser.js | 6 +- Specs/Scene/B3dmParserSpec.js | 89 +++++++++++++++++++ .../Scene/Batched3DModel3DTileContentSpec.js | 35 -------- .../Scene/ModelExperimental/B3dmLoaderSpec.js | 33 ------- 4 files changed, 94 insertions(+), 69 deletions(-) diff --git a/Source/Scene/B3dmParser.js b/Source/Scene/B3dmParser.js index 530927ca9671..8879fc5d9256 100644 --- a/Source/Scene/B3dmParser.js +++ b/Source/Scene/B3dmParser.js @@ -25,11 +25,15 @@ var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; * @returns {Object} Returns an object with the batch length, feature table (binary and json), batch table (binary and json) and glTF parts of the B3DM. */ B3dmParser.parse = function (arrayBuffer, byteOffset) { + var byteStart = defaultValue(byteOffset, 0); //>>includeStart('debug', pragmas.debug); Check.defined("arrayBuffer", arrayBuffer); + Check.typeOf.number("byteOffset", byteStart); + if (byteStart < 0) { + throw new RuntimeError("byteOffset cannot be less than 0."); + } //>>includeEnd('debug'); - var byteStart = defaultValue(byteOffset, 0); byteOffset = byteStart; var uint8Array = new Uint8Array(arrayBuffer); diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js index e69de29bb2d1..764a65660ddb 100644 --- a/Specs/Scene/B3dmParserSpec.js +++ b/Specs/Scene/B3dmParserSpec.js @@ -0,0 +1,89 @@ +import { + B3dmParser, + Cartesian3, + HeadingPitchRange, +} from "../../Source/Cesium.js"; +import Cesium3DTilesTester from "../Cesium3DTilesTester.js"; +import createScene from "../createScene.js"; + +describe("Scene/B3dmParser", function () { + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; + + var deprecated1Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json"; + var deprecated2Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated2/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(B3dmParser, "_deprecationWarning"); + }); + + afterAll(function () { + scene.destroyForSpecs(); + }); + + beforeEach(function () { + setCamera(centerLongitude, centerLatitude); + }); + + afterEach(function () { + scene.primitives.removeAll(); + }); + + it("throws on undefined arrayBuffer", function () { + expect(function () { + B3dmParser.parse(undefined); + }).toThrowDeveloperError(); + }); + + it("throws on invalid byteOffset", function () { + expect(function () { + B3dmParser.parse(new ArrayBuffer(1), "a"); + }).toThrowRuntimeError(); + expect(function () { + B3dmParser.parse(new ArrayBuffer(1), -1); + }).toThrowRuntimeError(); + }); + + it("recognizes the legacy 20-byte header", function () { + return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( + function (tileset) { + expect(B3dmParser._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(B3dmParser._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(B3dmParser._deprecationWarning).toHaveBeenCalled(); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + } + ); + }); +}); diff --git a/Specs/Scene/Batched3DModel3DTileContentSpec.js b/Specs/Scene/Batched3DModel3DTileContentSpec.js index cf68ea7fe653..c29806761b72 100644 --- a/Specs/Scene/Batched3DModel3DTileContentSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentSpec.js @@ -41,10 +41,6 @@ describe( "./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"; @@ -80,37 +76,6 @@ describe( Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "b3dm"); }); - it("recognizes the legacy 20-byte header", function () { - return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( - function (tileset) { - expect(B3dmParser._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(B3dmParser._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(B3dmParser._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(); diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 9757212ef2e1..1f82e84ef815 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -23,11 +23,6 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { "./Data/Cesium3DTiles/Batched/BatchedWithRtcCenter/batchedWithRtcCenter.b3dm"; var withBatchTableHierarchy = "./Data/Cesium3DTiles/Hierarchy/BatchTableHierarchy/tile.b3dm"; - var deprecated1Url = - "./Data/Cesium3DTiles/Batched/BatchedDeprecated1/batchedDeprecated1.b3dm"; - var deprecated2Url = - "./Data/Cesium3DTiles/Batched/BatchedDeprecated2/batchedDeprecated2.b3dm"; - var scene; var b3dmLoaders = []; @@ -144,34 +139,6 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "b3dm"); }); - it("recognizes the legacy 20-byte header", function () { - return loadB3dm(deprecated1Url).then(function (loader) { - expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); - - var components = loader.components; - var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); - }); - }); - - it("recognizes the legacy 24-byte header", function () { - return loadB3dm(deprecated2Url).then(function (loader) { - expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); - - var components = loader.components; - var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); - }); - }); - it("throws with empty gltf", function () { // Expect to throw DeveloperError in Model due to invalid gltf magic var arrayBuffer = Cesium3DTilesTester.generateBatchedTileBuffer(); From 4710c2b9130ac57f9da18a6af53d7155aad5cb2a Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 15:48:33 -0400 Subject: [PATCH 21/31] Fixes the error check --- Specs/Scene/B3dmParserSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js index 764a65660ddb..92c7ffa9b7e0 100644 --- a/Specs/Scene/B3dmParserSpec.js +++ b/Specs/Scene/B3dmParserSpec.js @@ -50,7 +50,7 @@ describe("Scene/B3dmParser", function () { it("throws on invalid byteOffset", function () { expect(function () { B3dmParser.parse(new ArrayBuffer(1), "a"); - }).toThrowRuntimeError(); + }).toThrowDeveloperError(); expect(function () { B3dmParser.parse(new ArrayBuffer(1), -1); }).toThrowRuntimeError(); From a537696f7e7437b2d9353314c5d56fc74e2a7cdc Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Tue, 19 Oct 2021 16:09:29 -0400 Subject: [PATCH 22/31] Add WebGL to B3dmParserSpec --- Specs/Scene/B3dmParserSpec.js | 142 +++++++++++++++++----------------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js index 92c7ffa9b7e0..7646882021e6 100644 --- a/Specs/Scene/B3dmParserSpec.js +++ b/Specs/Scene/B3dmParserSpec.js @@ -6,84 +6,88 @@ import { import Cesium3DTilesTester from "../Cesium3DTilesTester.js"; import createScene from "../createScene.js"; -describe("Scene/B3dmParser", function () { - var scene; - var centerLongitude = -1.31968; - var centerLatitude = 0.698874; +describe( + "Scene/B3dmParser", + function () { + var scene; + var centerLongitude = -1.31968; + var centerLatitude = 0.698874; - var deprecated1Url = - "./Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json"; - var deprecated2Url = - "./Data/Cesium3DTiles/Batched/BatchedDeprecated2/tileset.json"; + var deprecated1Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated1/tileset.json"; + var deprecated2Url = + "./Data/Cesium3DTiles/Batched/BatchedDeprecated2/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)); - } + 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(); + beforeAll(function () { + scene = createScene(); - // Keep the error from logging to the console when running tests - spyOn(B3dmParser, "_deprecationWarning"); - }); + // Keep the error from logging to the console when running tests + spyOn(B3dmParser, "_deprecationWarning"); + }); - afterAll(function () { - scene.destroyForSpecs(); - }); + afterAll(function () { + scene.destroyForSpecs(); + }); - beforeEach(function () { - setCamera(centerLongitude, centerLatitude); - }); + beforeEach(function () { + setCamera(centerLongitude, centerLatitude); + }); - afterEach(function () { - scene.primitives.removeAll(); - }); + afterEach(function () { + scene.primitives.removeAll(); + }); - it("throws on undefined arrayBuffer", function () { - expect(function () { - B3dmParser.parse(undefined); - }).toThrowDeveloperError(); - }); + it("throws on undefined arrayBuffer", function () { + expect(function () { + B3dmParser.parse(undefined); + }).toThrowDeveloperError(); + }); - it("throws on invalid byteOffset", function () { - expect(function () { - B3dmParser.parse(new ArrayBuffer(1), "a"); - }).toThrowDeveloperError(); - expect(function () { - B3dmParser.parse(new ArrayBuffer(1), -1); - }).toThrowRuntimeError(); - }); + it("throws on invalid byteOffset", function () { + expect(function () { + B3dmParser.parse(new ArrayBuffer(1), "a"); + }).toThrowDeveloperError(); + expect(function () { + B3dmParser.parse(new ArrayBuffer(1), -1); + }).toThrowRuntimeError(); + }); - it("recognizes the legacy 20-byte header", function () { - return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( - function (tileset) { - expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); - Cesium3DTilesTester.expectRenderTileset(scene, tileset); - var batchTable = tileset.root.content.batchTable; - expect(batchTable._properties).toBeDefined(); - } - ); - }); + it("recognizes the legacy 20-byte header", function () { + return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( + function (tileset) { + expect(B3dmParser._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(B3dmParser._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(B3dmParser._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(B3dmParser._deprecationWarning).toHaveBeenCalled(); - Cesium3DTilesTester.expectRenderTileset(scene, tileset); - } - ); - }); -}); + it("logs deprecation warning for use of BATCHID without prefixed underscore", function () { + return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( + function (tileset) { + expect(B3dmParser._deprecationWarning).toHaveBeenCalled(); + Cesium3DTilesTester.expectRenderTileset(scene, tileset); + } + ); + }); + }, + "WebGL" +); From a52b683866109ac308c85cbf6b00ff0f819d22e9 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 20 Oct 2021 13:09:07 -0400 Subject: [PATCH 23/31] Adds more checks --- .../ModelExperimental/ModelExperimental3DTileContentSpec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js index 7985e01f06d4..3c15052c8b5d 100644 --- a/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js +++ b/Specs/Scene/ModelExperimental/ModelExperimental3DTileContentSpec.js @@ -128,6 +128,7 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); + expect(content.batchTable).toBeDefined(); expect(content.hasProperty(0, "id")).toBe(true); expect(content.getFeature(0)).toBeDefined(); }); @@ -147,6 +148,7 @@ describe("Scene/ModelExperimental/ModelExperimental3DTileContentSpec", function expect(result).toBeDefined(); expect(result.primitive).toBe(tileset); expect(result.content).toBe(content); + expect(content.batchTable).toBeDefined(); expect(content.hasProperty(0, "id")).toBe(true); expect(content.getFeature(0)).toBeDefined(); }); From c4eda3a2e8702e39c7df6716e0faddb2556db786 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 20 Oct 2021 13:43:09 -0400 Subject: [PATCH 24/31] more spec fixes --- Specs/Scene/B3dmParserSpec.js | 18 +++++++++++++++++- Specs/Scene/Batched3DModel3DTileContentSpec.js | 13 ------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js index 7646882021e6..c14c28af2d62 100644 --- a/Specs/Scene/B3dmParserSpec.js +++ b/Specs/Scene/B3dmParserSpec.js @@ -43,16 +43,32 @@ describe( scene.primitives.removeAll(); }); + it("throws with invalid version", function () { + var arrayBuffer = Cesium3DTilesTester.generateBatchedTileBuffer({ + version: 2, + }); + Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "b3dm"); + }); + + 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("throws on undefined arrayBuffer", function () { expect(function () { B3dmParser.parse(undefined); }).toThrowDeveloperError(); }); - it("throws on invalid byteOffset", function () { + it("throws on invalid type in byteOffset", function () { expect(function () { B3dmParser.parse(new ArrayBuffer(1), "a"); }).toThrowDeveloperError(); + }); + + it("throws on invalid byteOffset", function () { expect(function () { B3dmParser.parse(new ArrayBuffer(1), -1); }).toThrowRuntimeError(); diff --git a/Specs/Scene/Batched3DModel3DTileContentSpec.js b/Specs/Scene/Batched3DModel3DTileContentSpec.js index c29806761b72..85b117c1fdeb 100644 --- a/Specs/Scene/Batched3DModel3DTileContentSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentSpec.js @@ -69,19 +69,6 @@ describe( scene.primitives.removeAll(); }); - it("throws with invalid version", function () { - var arrayBuffer = Cesium3DTilesTester.generateBatchedTileBuffer({ - version: 2, - }); - Cesium3DTilesTester.loadTileExpectError(scene, arrayBuffer, "b3dm"); - }); - - 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, From bbf93c2d97ea4d09347f2992639dcd109d265aad Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Wed, 20 Oct 2021 14:15:49 -0400 Subject: [PATCH 25/31] Removes broken spec --- Source/Scene/B3dmParser.js | 4 ---- Specs/Scene/B3dmParserSpec.js | 12 ------------ 2 files changed, 16 deletions(-) diff --git a/Source/Scene/B3dmParser.js b/Source/Scene/B3dmParser.js index 8879fc5d9256..34c664ceb9e2 100644 --- a/Source/Scene/B3dmParser.js +++ b/Source/Scene/B3dmParser.js @@ -28,10 +28,6 @@ B3dmParser.parse = function (arrayBuffer, byteOffset) { var byteStart = defaultValue(byteOffset, 0); //>>includeStart('debug', pragmas.debug); Check.defined("arrayBuffer", arrayBuffer); - Check.typeOf.number("byteOffset", byteStart); - if (byteStart < 0) { - throw new RuntimeError("byteOffset cannot be less than 0."); - } //>>includeEnd('debug'); byteOffset = byteStart; diff --git a/Specs/Scene/B3dmParserSpec.js b/Specs/Scene/B3dmParserSpec.js index c14c28af2d62..25b7665b817d 100644 --- a/Specs/Scene/B3dmParserSpec.js +++ b/Specs/Scene/B3dmParserSpec.js @@ -62,18 +62,6 @@ describe( }).toThrowDeveloperError(); }); - it("throws on invalid type in byteOffset", function () { - expect(function () { - B3dmParser.parse(new ArrayBuffer(1), "a"); - }).toThrowDeveloperError(); - }); - - it("throws on invalid byteOffset", function () { - expect(function () { - B3dmParser.parse(new ArrayBuffer(1), -1); - }).toThrowRuntimeError(); - }); - it("recognizes the legacy 20-byte header", function () { return Cesium3DTilesTester.loadTileset(scene, deprecated1Url).then( function (tileset) { From 25708b423d6feda733bc19c73e8aae79e2ca78ed Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 09:07:22 -0400 Subject: [PATCH 26/31] more updates --- Source/Scene/ModelExperimental/B3dmLoader.js | 47 +++++++++++++++-- .../FeatureIdPipelineStage.js | 52 ++++++++----------- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 64593a252c39..ae4d312a6134 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -12,9 +12,12 @@ import FeatureMetadata from "../FeatureMetadata.js"; import GltfLoader from "../GltfLoader.js"; import Matrix4 from "../../Core/Matrix4.js"; import MetadataClass from "../MetadataClass.js"; +import ModelComponents from "../ModelComponents.js"; +import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import parseBatchTable from "../parseBatchTable.js"; import ResourceLoader from "../ResourceLoader.js"; import when from "../../ThirdParty/when.js"; +import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; var B3dmLoaderState = { UNLOADED: 0, @@ -25,6 +28,8 @@ var B3dmLoaderState = { FAILED: 5, }; +var FeatureIdAttribute = ModelComponents.FeatureIdAttribute; + /** * Loads a Batched 3D Model. *

@@ -92,7 +97,7 @@ function B3dmLoader(options) { this._featureTable = undefined; // The batch table object contains a json and a binary component access using keys of the same name. - this._batc = undefined; + this._batchTable = undefined; this._components = undefined; this._rtcTransform = undefined; } @@ -123,7 +128,7 @@ Object.defineProperties(B3dmLoader.prototype, { * When incrementallyLoadTextures is true this may resolve after * promise resolves. * - * @memberof GltfLoader.prototype + * @memberof B3dmLoader.prototype * * @type {Promise} * @readonly @@ -186,7 +191,7 @@ Object.defineProperties(B3dmLoader.prototype, { * @private */ B3dmLoader.prototype.load = function () { - var b3dm = B3dmParser.parse(this._arrayBuffer, this._offset); + var b3dm = B3dmParser.parse(this._arrayBuffer, this._byteOffset); var batchLength = b3dm.batchLength; var featureTableJson = b3dm.featureTableJson; @@ -295,6 +300,11 @@ function processGltfComponents(loader, components) { batchTable: batchTable.json, binaryBody: batchTable.binary, }); + // Add the feature ID attribute to the primitive. + var nodes = components.scene.nodes; + for (var i = 0; i < nodes.length; i++) { + processNode(nodes[i]); + } } else { // If batch table is not defined, create a feature table without any properties. var emptyFeatureTable = new FeatureTable({ @@ -310,6 +320,37 @@ function processGltfComponents(loader, components) { components.featureMetadata = featureMetadata; } +function processNode(node) { + if (!defined(node.children) && !defined(node.primitives)) { + return; + } + + var i; + if (defined(node.children)) { + for (i = 0; i < node.children.length; i++) { + processNode(node.children[i]); + } + } + + if (defined(node.primitives)) { + for (i = 0; i < node.primitives.length; i++) { + var primitive = node.primitives[i]; + var featureIdVertexAttribute = ModelExperimentalUtility.getAttributeBySemantic( + primitive, + VertexAttributeSemantic.FEATURE_ID + ); + if (defined(featureIdVertexAttribute)) { + featureIdVertexAttribute.setIndex = 0; + var featureIdAttribute = new FeatureIdAttribute(); + featureIdAttribute.featureTableId = + MetadataClass.BATCH_TABLE_CLASS_NAME; + featureIdAttribute.setIndex = 0; + primitive.featureIdAttributes.push(featureIdAttribute); + } + } + } +} + B3dmLoader.prototype.unload = function () { if (defined(this._gltfLoader)) { this._gltfLoader.unload(); diff --git a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js index db161365303f..d8ea90d91691 100644 --- a/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js +++ b/Source/Scene/ModelExperimental/FeatureIdPipelineStage.js @@ -100,40 +100,32 @@ function processFeatureIdAttributes(renderResources, frameState, primitive) { var model = renderResources.model; var instances = renderResources.runtimeNode.node.instances; - var featureIdAttributePrefix; + var featureIdAttributeIndex = model.featureIdAttributeIndex; + + var featureIdAttributeInfo = getFeatureIdAttributeInfo( + featureIdAttributeIndex, + primitive, + instances + ); + + var featureIdAttribute = featureIdAttributeInfo.attribute; + var featureIdAttributePrefix = featureIdAttributeInfo.prefix; var featureIdAttributeSetIndex; - // For 3D Tiles 1.0, the FEATURE_ID vertex attribute is present but the Feature ID attribute is not. - if (primitive.featureIdAttributes.length === 0 && !defined(instances)) { - featureIdAttributePrefix = "a_featureId"; - featureIdAttributeSetIndex = ""; + // Check if the Feature ID attribute references an existing vertex attribute. + if (defined(featureIdAttribute.setIndex)) { + featureIdAttributeSetIndex = featureIdAttribute.setIndex; } else { - var featureIdAttributeIndex = model.featureIdAttributeIndex; - - var featureIdAttributeInfo = getFeatureIdAttributeInfo( - featureIdAttributeIndex, - primitive, - instances + // Ensure that the new Feature ID vertex attribute generated does not have any conflicts with + // Feature ID vertex attributes already provided in the model. The featureIdVertexAttributeSetIndex + // is incremented every time a Feature ID vertex attribute is added. + featureIdAttributeSetIndex = renderResources.featureIdVertexAttributeSetIndex++; + generateFeatureIdAttribute( + featureIdAttributeInfo, + featureIdAttributeSetIndex, + frameState, + renderResources ); - - var featureIdAttribute = featureIdAttributeInfo.attribute; - featureIdAttributePrefix = featureIdAttributeInfo.prefix; - - // Check if the Feature ID attribute references an existing vertex attribute. - if (defined(featureIdAttribute.setIndex)) { - featureIdAttributeSetIndex = featureIdAttribute.setIndex; - } else { - // Ensure that the new Feature ID vertex attribute generated does not have any conflicts with - // Feature ID vertex attributes already provided in the model. The featureIdVertexAttributeSetIndex - // is incremented every time a Feature ID vertex attribute is added. - featureIdAttributeSetIndex = renderResources.featureIdVertexAttributeSetIndex++; - generateFeatureIdAttribute( - featureIdAttributeInfo, - featureIdAttributeSetIndex, - frameState, - renderResources - ); - } } shaderBuilder.addDefine( From dbe47e78b343eea66ab3a9681ccfefbe0529189a Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 17:06:33 -0400 Subject: [PATCH 27/31] Addresses feedback --- Source/Scene/Gltf3DTileContent.js | 2 +- Source/Scene/GltfLoader.js | 17 +++ Source/Scene/ModelExperimental/B3dmLoader.js | 108 ++++++++---------- .../ModelExperimental/ModelExperimental.js | 15 +-- .../ModelExperimental3DTileContent.js | 4 - .../ModelExperimental/ModelRenderResources.js | 11 -- .../ModelExperimental/NodeRenderResources.js | 11 -- .../PrimitiveRenderResources.js | 11 -- Specs/Scene/GltfLoaderSpec.js | 6 + .../Scene/ModelExperimental/B3dmLoaderSpec.js | 2 +- 10 files changed, 81 insertions(+), 106 deletions(-) diff --git a/Source/Scene/Gltf3DTileContent.js b/Source/Scene/Gltf3DTileContent.js index e0378cba4bdb..9054ea6caa22 100644 --- a/Source/Scene/Gltf3DTileContent.js +++ b/Source/Scene/Gltf3DTileContent.js @@ -8,7 +8,7 @@ import Model from "./Model.js"; import ModelAnimationLoop from "./ModelAnimationLoop.js"; /** - * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf/0.0.0|3DTILES_content_gltf} extension. + * Represents the contents of a glTF or glb tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset using the {@link https://github.com/CesiumGS/3d-tiles/tree/3d-tiles-next/extensions/3DTILES_content_gltf/|3DTILES_content_gltf} extension. *

* Implements the {@link Cesium3DTileContent} interface. *

diff --git a/Source/Scene/GltfLoader.js b/Source/Scene/GltfLoader.js index 1028d8fbc03e..c86c2e7eb609 100644 --- a/Source/Scene/GltfLoader.js +++ b/Source/Scene/GltfLoader.js @@ -107,6 +107,7 @@ export default function GltfLoader(options) { this._textureState = GltfLoaderState.UNLOADED; this._promise = when.defer(); this._texturesLoadedPromise = when.defer(); + this._transform = Matrix4.IDENTITY; // Loaders that need to be processed before the glTF becomes ready this._textureLoaders = []; @@ -183,6 +184,22 @@ Object.defineProperties(GltfLoader.prototype, { return this._texturesLoadedPromise.promise; }, }, + + /** + * A world-space transform to apply to the primitives. + * + * @memberof GltfLoader.prototype + * + * @type {Matrix4} + * @default {@link Matrix4.IDENTITY} + * @readonly + * @private + */ + transform: { + get: function () { + return this._transform; + }, + }, }); /** diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index ae4d312a6134..ed944bfe95f4 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -6,7 +6,6 @@ import Check from "../../Core/Check.js"; import ComponentDatatype from "../../Core/ComponentDatatype.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; -import deprecationWarning from "../../Core/deprecationWarning.js"; import FeatureTable from "../FeatureTable.js"; import FeatureMetadata from "../FeatureMetadata.js"; import GltfLoader from "../GltfLoader.js"; @@ -22,10 +21,9 @@ import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; var B3dmLoaderState = { UNLOADED: 0, LOADING: 1, - LOADED: 2, - PROCESSING: 3, - READY: 4, - FAILED: 5, + PROCESSING: 2, + READY: 3, + FAILED: 4, }; var FeatureIdAttribute = ModelComponents.FeatureIdAttribute; @@ -88,7 +86,6 @@ function B3dmLoader(options) { this._state = B3dmLoaderState.UNLOADED; this._promise = when.defer(); - this._texturesLoadedPromise = when.defer(); this._gltfLoader = undefined; @@ -99,7 +96,7 @@ function B3dmLoader(options) { // The batch table object contains a json and a binary component access using keys of the same name. this._batchTable = undefined; this._components = undefined; - this._rtcTransform = undefined; + this._transform = Matrix4.IDENTITY; } if (defined(Object.create)) { @@ -136,7 +133,7 @@ Object.defineProperties(B3dmLoader.prototype, { */ texturesLoadedPromise: { get: function () { - return this._texturesLoadedPromise.promise; + return this._gltfLoader.texturesLoadedPromise; }, }, /** @@ -160,6 +157,7 @@ Object.defineProperties(B3dmLoader.prototype, { * @memberof B3dmLoader.prototype * * @type {ModelComponents.Components} + * @default {@link Matrix4.IDENTITY} * @readonly * @private */ @@ -170,7 +168,7 @@ Object.defineProperties(B3dmLoader.prototype, { }, /** - * A transform from the RTC_CENTER semantic, if present in the B3DM's Feature Table. + * A world-space transform to apply to the primitives. * See {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Batched3DModel#global-semantics} * * @memberof B3dmLoader.prototype @@ -179,9 +177,9 @@ Object.defineProperties(B3dmLoader.prototype, { * @readonly * @private */ - rtcTransform: { + transform: { get: function () { - return this._rtcTransform; + return this._transform; }, }, }); @@ -213,9 +211,7 @@ B3dmLoader.prototype.load = function () { 3 ); if (defined(rtcCenter)) { - this._rtcTransform = Matrix4.fromTranslation( - Cartesian3.fromArray(rtcCenter) - ); + this._transform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); } this._batchTable = { @@ -236,49 +232,20 @@ B3dmLoader.prototype.load = function () { this._gltfLoader = gltfLoader; this._state = B3dmLoaderState.LOADING; - gltfLoader.load(); -}; - -function handleError(gltfLoader, error) { - gltfLoader.unload(); - gltfLoader._state = B3dmLoaderState.FAILED; - var errorMessage = "Failed to load B3DM"; - error = gltfLoader.getError(errorMessage, error); - gltfLoader._promise.reject(error); -} -B3dmLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - this._state = B3dmLoaderState.PROCESSING; - var gltfLoader = this._gltfLoader; - - // Once the components are loaded, add the feature metadata created from the batch table. var that = this; - gltfLoader.process(frameState); - gltfLoader.promise.then(function () { - if (that.isDestroyed()) { - return; - } - - if (that._state !== B3dmLoaderState.READY) { - var components = gltfLoader.components; - processGltfComponents(that, components); - that._components = components; - that._state = B3dmLoaderState.READY; - } - - that._promise.resolve(that); - }); - - gltfLoader.texturesLoadedPromise + gltfLoader.load(); + gltfLoader.promise .then(function () { if (that.isDestroyed()) { return; } - that._texturesLoadedPromise.resolve(that); + var components = gltfLoader.components; + createFeatureMetadata(that, components); + that._components = components; + + that._state = B3dmLoaderState.READY; + that._promise.resolve(that); }) .otherwise(function (error) { if (that.isDestroyed()) { @@ -288,7 +255,29 @@ B3dmLoader.prototype.process = function (frameState) { }); }; -function processGltfComponents(loader, components) { +function handleError(b3dmLoader, error) { + b3dmLoader.unload(); + b3dmLoader._state = B3dmLoaderState.FAILED; + var errorMessage = "Failed to load B3DM"; + error = b3dmLoader.getError(errorMessage, error); + b3dmLoader._promise.reject(error); +} + +B3dmLoader.prototype.process = function (frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === B3dmLoaderState.LOADING) { + this._state = B3dmLoaderState.PROCESSING; + } + + if (this._state === B3dmLoaderState.PROCESSING) { + this._gltfLoader.process(frameState); + } +}; + +function createFeatureMetadata(loader, components) { var batchTable = loader._batchTable; var batchLength = loader._batchLength; @@ -300,11 +289,6 @@ function processGltfComponents(loader, components) { batchTable: batchTable.json, binaryBody: batchTable.binary, }); - // Add the feature ID attribute to the primitive. - var nodes = components.scene.nodes; - for (var i = 0; i < nodes.length; i++) { - processNode(nodes[i]); - } } else { // If batch table is not defined, create a feature table without any properties. var emptyFeatureTable = new FeatureTable({ @@ -317,9 +301,16 @@ function processGltfComponents(loader, components) { featureTables: featureTables, }); } + + // Add the feature ID attribute to the primitives. + var nodes = components.scene.nodes; + for (var i = 0; i < nodes.length; i++) { + processNode(nodes[i]); + } components.featureMetadata = featureMetadata; } +// Recursive function to add the feature ID attribute to all primitives that have a feature ID vertex attribute. function processNode(node) { if (!defined(node.children) && !defined(node.primitives)) { return; @@ -359,7 +350,4 @@ B3dmLoader.prototype.unload = function () { this._components = undefined; }; -// This can be overridden for testing purposes -B3dmLoader._deprecationWarning = deprecationWarning; - export default B3dmLoader; diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 567a593ed2a2..2c33cf7af644 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -57,7 +57,9 @@ export default function ModelExperimental(options) { this._loader = options.loader; this._resource = options.resource; - this._modelMatrix = defaultValue(options.modelMatrix, Matrix4.IDENTITY); + this._modelMatrix = Matrix4.clone( + defaultValue(options.modelMatrix, Matrix4.IDENTITY) + ); this._resourcesLoaded = false; this._drawCommandsBuilt = false; @@ -169,16 +171,16 @@ function selectFeatureTableId(components, model) { function initialize(model) { var loader = model._loader; var resource = model._resource; - var modelMatrix = model._modelMatrix; loader.load(); loader.promise .then(function (loader) { - var rtcTransform = loader.rtcTransform; - if (defined(rtcTransform)) { - Matrix4.multiply(modelMatrix, rtcTransform, modelMatrix); - } + Matrix4.multiply( + model._modelMatrix, + loader.transform, + model._modelMatrix + ); var components = loader.components; var content = model._content; @@ -197,7 +199,6 @@ function initialize(model) { model._sceneGraph = new ModelExperimentalSceneGraph({ model: model, modelComponents: components, - modelMatrix: modelMatrix, }); model._resourcesLoaded = true; }) diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index 3c37a593d6d8..b53831a60263 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -172,7 +172,6 @@ ModelExperimental3DTileContent.prototype.update = function ( var tile = this._tile; model.modelMatrix = tile.computedTransform; - model.backFaceCulling = tileset.backFaceCulling; model.update(frameState); }; @@ -237,6 +236,3 @@ ModelExperimental3DTileContent.fromB3dm = function ( content._model = ModelExperimental.fromB3dm(modelOptions); return content; }; - -// This can be overridden for testing purposes -ModelExperimental3DTileContent._deprecationWarning = deprecationWarning; diff --git a/Source/Scene/ModelExperimental/ModelRenderResources.js b/Source/Scene/ModelExperimental/ModelRenderResources.js index b6be4082aac1..943a32fc92f5 100644 --- a/Source/Scene/ModelExperimental/ModelRenderResources.js +++ b/Source/Scene/ModelExperimental/ModelRenderResources.js @@ -35,15 +35,4 @@ export default function ModelRenderResources(model) { * @private */ this.model = model; - /** - * The feature table ID to use for determining features within the model. - * - * @type {String} - * @readonly - * - * @private - */ - this.featureTableId = defined(model.content) - ? model.content.featureTableId - : model.featureTableId; } diff --git a/Source/Scene/ModelExperimental/NodeRenderResources.js b/Source/Scene/ModelExperimental/NodeRenderResources.js index aafede937840..644785681fe6 100644 --- a/Source/Scene/ModelExperimental/NodeRenderResources.js +++ b/Source/Scene/ModelExperimental/NodeRenderResources.js @@ -40,17 +40,6 @@ export default function NodeRenderResources(modelRenderResources, runtimeNode) { */ this.shaderBuilder = modelRenderResources.shaderBuilder.clone(); - /** - * The ID of the feature table to use for picking and styling. Inherited from the model - * render resources. - * - * @type {String} - * @readonly - * - * @private - */ - this.featureTableId = modelRenderResources.featureTableId; - // other properties /** * A reference to the runtime node diff --git a/Source/Scene/ModelExperimental/PrimitiveRenderResources.js b/Source/Scene/ModelExperimental/PrimitiveRenderResources.js index 5c8ad2f9e8d8..ff5b5a5c3cda 100644 --- a/Source/Scene/ModelExperimental/PrimitiveRenderResources.js +++ b/Source/Scene/ModelExperimental/PrimitiveRenderResources.js @@ -86,17 +86,6 @@ export default function PrimitiveRenderResources( */ this.hasFeatureIds = false; - /** - * The ID of the feature table to use for picking and styling. Inherited from the node - * render resources. - * - * @type {String} - * @readonly - * - * @private - */ - this.featureTableId = nodeRenderResources.featureTableId; - /** * The computed model matrix for this primitive. This is cloned from the * node render resources as the primitive may further modify it diff --git a/Specs/Scene/GltfLoaderSpec.js b/Specs/Scene/GltfLoaderSpec.js index f77c6edec9f4..2fe3e8ce9b08 100644 --- a/Specs/Scene/GltfLoaderSpec.js +++ b/Specs/Scene/GltfLoaderSpec.js @@ -1875,6 +1875,12 @@ describe( }); }); + it("sets default transform", function () { + return loadGltf(microcosm).then(function (gltfLoader) { + expect(gltfLoader.transform).toEqual(Matrix4.IDENTITY); + }); + }); + it("destroys glTF loader", function () { var destroyFeatureMetadataLoader = spyOn( GltfFeatureMetadataLoader.prototype, diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 1f82e84ef815..98d2e867ba49 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -113,7 +113,7 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { expect(featureTable).toBeDefined(); expect(featureTable.count).toEqual(10); - expect(loader.rtcTransform).toEqual( + expect(loader.transform).toEqual( Matrix4.fromTranslation(new Cartesian3(0.1, 0.2, 0.3)) ); }); From 326319a71c767531ceba34b3bfa3b61438598098 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 17:47:23 -0400 Subject: [PATCH 28/31] Eslint fixes --- Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js | 1 - Source/Scene/ModelExperimental/ModelRenderResources.js | 1 - 2 files changed, 2 deletions(-) diff --git a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js index b53831a60263..fca02da38385 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js +++ b/Source/Scene/ModelExperimental/ModelExperimental3DTileContent.js @@ -1,6 +1,5 @@ import Axis from "../Axis.js"; import defined from "../../Core/defined.js"; -import deprecationWarning from "../../Core/deprecationWarning.js"; import destroyObject from "../../Core/destroyObject.js"; import ModelExperimental from "./ModelExperimental.js"; import Pass from "../../Renderer/Pass.js"; diff --git a/Source/Scene/ModelExperimental/ModelRenderResources.js b/Source/Scene/ModelExperimental/ModelRenderResources.js index 943a32fc92f5..c4a06fb8dcad 100644 --- a/Source/Scene/ModelExperimental/ModelRenderResources.js +++ b/Source/Scene/ModelExperimental/ModelRenderResources.js @@ -1,5 +1,4 @@ import Check from "../../Core/Check.js"; -import defined from "../../Core/defined.js"; import ShaderBuilder from "../../Renderer/ShaderBuilder.js"; /** From b6f6a7c26c6fa418e9f83511b528c9eb2fe82862 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 18:46:38 -0400 Subject: [PATCH 29/31] Merge fixes --- Source/Scene/ModelExperimental/B3dmLoader.js | 15 +++--- .../ModelExperimental/ModelExperimental.js | 34 +++---------- .../ModelExperimental/ModelFeatureTable.js | 15 +++--- .../Scene/ModelExperimental/B3dmLoaderSpec.js | 48 ++++++++----------- .../BatchTexturePipelineStageSpec.js | 2 +- 5 files changed, 42 insertions(+), 72 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index ed944bfe95f4..b87eb17ad6c3 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -6,7 +6,6 @@ import Check from "../../Core/Check.js"; import ComponentDatatype from "../../Core/ComponentDatatype.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; -import FeatureTable from "../FeatureTable.js"; import FeatureMetadata from "../FeatureMetadata.js"; import GltfLoader from "../GltfLoader.js"; import Matrix4 from "../../Core/Matrix4.js"; @@ -14,6 +13,7 @@ import MetadataClass from "../MetadataClass.js"; import ModelComponents from "../ModelComponents.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import parseBatchTable from "../parseBatchTable.js"; +import PropertyTable from "../PropertyTable.js"; import ResourceLoader from "../ResourceLoader.js"; import when from "../../ThirdParty/when.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; @@ -91,7 +91,7 @@ function B3dmLoader(options) { // Loaded results. this._batchLength = 0; - this._featureTable = undefined; + this._propertyTable = undefined; // The batch table object contains a json and a binary component access using keys of the same name. this._batchTable = undefined; @@ -290,15 +290,13 @@ function createFeatureMetadata(loader, components) { binaryBody: batchTable.binary, }); } else { - // If batch table is not defined, create a feature table without any properties. - var emptyFeatureTable = new FeatureTable({ + // If batch table is not defined, create a property table without any properties. + var emptyPropertyTable = new PropertyTable({ count: batchLength, }); - var featureTables = {}; - featureTables[MetadataClass.BATCH_TABLE_CLASS_NAME] = emptyFeatureTable; featureMetadata = new FeatureMetadata({ schema: {}, - featureTables: featureTables, + propertyTables: [emptyPropertyTable], }); } @@ -333,8 +331,7 @@ function processNode(node) { if (defined(featureIdVertexAttribute)) { featureIdVertexAttribute.setIndex = 0; var featureIdAttribute = new FeatureIdAttribute(); - featureIdAttribute.featureTableId = - MetadataClass.BATCH_TABLE_CLASS_NAME; + featureIdAttribute.propertyTableId = 0; featureIdAttribute.setIndex = 0; primitive.featureIdAttributes.push(featureIdAttribute); } diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index a1e27364b915..163243dbf136 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -119,13 +119,6 @@ function createModelFeatureTables(model, featureMetadata) { } function selectFeatureTableId(components, model) { - var featureTables = model._featureTables; - - // For 3D Tiles 1.0 formats, the feature table will always be the first (and only) feature table. - if (defined(featureTables[MetadataClass.BATCH_TABLE_CLASS_NAME])) { - return 0; - } - var featureIdAttributeIndex = model._featureIdAttributeIndex; var featureIdTextureIndex = model._featureIdTextureIndex; @@ -181,26 +174,13 @@ function initialize(model) { ); var components = loader.components; - var content = model._content; - - // For 3D Tiles 1.0 formats, the feature metadata is owned by the Cesium3DTileContent classes. - // Otherwise, the metadata is owned by ModelExperimental. - var hasContent = defined(content); - var propertyTableOwner = hasContent ? content : model; - var featureMetadata = defined(propertyTableOwner.featureMetadata) - ? content.featureMetadata - : components.featureMetadata; + var featureMetadata = components.featureMetadata; if (defined(featureMetadata) && featureMetadata.propertyTableCount > 0) { - var featureTableId = selectFeatureTableId(components, model, content); - var featureTables; - if (hasContent) { - featureTables = createContentFeatureTables(content, featureMetadata); - } else { - featureTables = createModelFeatureTables(model, featureMetadata); - } - propertyTableOwner.featureTables = featureTables; - propertyTableOwner.featureTableId = featureTableId; + var featureTableId = selectFeatureTableId(components, model); + var featureTables = createModelFeatureTables(model, featureMetadata); + model.featureTables = featureTables; + model.featureTableId = featureTableId; } model._sceneGraph = new ModelExperimentalSceneGraph({ @@ -325,7 +305,7 @@ Object.defineProperties(ModelExperimental.prototype, { * * @memberof ModelExperimental.prototype * - * @type {String} + * @type {Number} * @readonly * * @private @@ -344,7 +324,7 @@ Object.defineProperties(ModelExperimental.prototype, { * * @memberof ModelExperimental.prototype * - * @type {Object.} + * @type {Array} * @readonly * * @private diff --git a/Source/Scene/ModelExperimental/ModelFeatureTable.js b/Source/Scene/ModelExperimental/ModelFeatureTable.js index caf16c99893e..e5f5deede960 100644 --- a/Source/Scene/ModelExperimental/ModelFeatureTable.js +++ b/Source/Scene/ModelExperimental/ModelFeatureTable.js @@ -11,7 +11,7 @@ import ModelFeature from "./ModelFeature.js"; * * @param {Object} options An object containing the following options: * @param {ModelExperimental} options.model The model that owns this feature table. - * @param {PropertyTable} options.propertyTable The feature table from the model used to initialize the model. + * @param {PropertyTable} options.propertyTable The property table from the model used to initialize the model. * * @alias ModelFeatureTable * @constructor @@ -20,14 +20,17 @@ import ModelFeature from "./ModelFeature.js"; * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. */ export default function ModelFeatureTable(options) { - this._propertyTable = options.propertyTable; - this._model = options.model; + var model = options.model; + var propertyTable = options.propertyTable; //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.featureTable", options.featureTable); - Check.typeOf.object("options.model", options.model); + Check.typeOf.object("propertyTable", propertyTable); + Check.typeOf.object("model", model); //>>includeEnd('debug'); + this._propertyTable = propertyTable; + this._model = model; + this._features = undefined; this._featuresLength = 0; @@ -74,7 +77,7 @@ function initialize(modelFeatureTable) { var content = modelFeatureTable._model.content; var hasContent = defined(content); - var featuresLength = modelFeatureTable._featureTable.count; + var featuresLength = modelFeatureTable._propertyTable.count; if (featuresLength === 0) { return; } diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 98d2e867ba49..40426fd93090 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -68,12 +68,10 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { return loadB3dm(withBatchTableUrl).then(function (loader) { var components = loader.components; var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); - expect(featureTable.class).toBeDefined(); + var propertyTable = featureMetadata.getPropertyTable(0); + expect(propertyTable).toBeDefined(); + expect(propertyTable.count).toEqual(10); + expect(propertyTable.class).toBeDefined(); }); }); @@ -81,12 +79,10 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { return loadB3dm(withBatchTableBinaryUrl).then(function (loader) { var components = loader.components; var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); - expect(featureTable.class).toBeDefined(); + var propertyTable = featureMetadata.getPropertyTable(0); + expect(propertyTable).toBeDefined(); + expect(propertyTable.count).toEqual(10); + expect(propertyTable.class).toBeDefined(); }); }); @@ -94,12 +90,10 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { return loadB3dm(withoutBatchTableUrl).then(function (loader) { var components = loader.components; var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); - expect(featureTable.class).toBeUndefined(); + var propertyTable = featureMetadata.getPropertyTable(0); + expect(propertyTable).toBeDefined(); + expect(propertyTable.count).toEqual(10); + expect(propertyTable.class).toBeUndefined(); }); }); @@ -107,11 +101,9 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { return loadB3dm(withRtcCenterUrl).then(function (loader) { var components = loader.components; var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(10); + var propertyTable = featureMetadata.getPropertyTable(0); + expect(propertyTable).toBeDefined(); + expect(propertyTable.count).toEqual(10); expect(loader.transform).toEqual( Matrix4.fromTranslation(new Cartesian3(0.1, 0.2, 0.3)) @@ -123,12 +115,10 @@ describe("Scene/ModelExperimental/B3dmLoader", function () { return loadB3dm(withBatchTableHierarchy).then(function (loader) { var components = loader.components; var featureMetadata = components.featureMetadata; - var featureTable = featureMetadata.getFeatureTable( - MetadataClass.BATCH_TABLE_CLASS_NAME - ); - expect(featureTable).toBeDefined(); - expect(featureTable.count).toEqual(30); - expect(featureTable._batchTableHierarchy).toBeDefined(); + var propertyTable = featureMetadata.getPropertyTable(0); + expect(propertyTable).toBeDefined(); + expect(propertyTable.count).toEqual(30); + expect(propertyTable._batchTableHierarchy).toBeDefined(); }); }); diff --git a/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js b/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js index 4cea67adc60e..0e1fb6cb3a71 100644 --- a/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js +++ b/Specs/Scene/ModelExperimental/BatchTexturePipelineStageSpec.js @@ -54,8 +54,8 @@ describe("Scene/ModelExperimental/BatchTexturePipelineStage", function () { it("sets up batch textures from ModelExperimental", function () { var renderResources = { shaderBuilder: new ShaderBuilder(), - featureTableId: 0, model: { + featureTableId: 0, featureTables: [ { featuresLength: 10, From 00c4c71a18495598453084e79d4f89c067aef7d9 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 18:50:50 -0400 Subject: [PATCH 30/31] Eslint fixes --- Source/Scene/ModelExperimental/B3dmLoader.js | 1 - Source/Scene/ModelExperimental/ModelExperimental.js | 1 - Specs/Scene/ModelExperimental/B3dmLoaderSpec.js | 1 - 3 files changed, 3 deletions(-) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index b87eb17ad6c3..89f3bcdaa09d 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -9,7 +9,6 @@ import defined from "../../Core/defined.js"; import FeatureMetadata from "../FeatureMetadata.js"; import GltfLoader from "../GltfLoader.js"; import Matrix4 from "../../Core/Matrix4.js"; -import MetadataClass from "../MetadataClass.js"; import ModelComponents from "../ModelComponents.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import parseBatchTable from "../parseBatchTable.js"; diff --git a/Source/Scene/ModelExperimental/ModelExperimental.js b/Source/Scene/ModelExperimental/ModelExperimental.js index 163243dbf136..36454e5fe3b0 100644 --- a/Source/Scene/ModelExperimental/ModelExperimental.js +++ b/Source/Scene/ModelExperimental/ModelExperimental.js @@ -11,7 +11,6 @@ import when from "../../ThirdParty/when.js"; import destroyObject from "../../Core/destroyObject.js"; import Matrix4 from "../../Core/Matrix4.js"; import ModelFeatureTable from "./ModelFeatureTable.js"; -import MetadataClass from "../MetadataClass.js"; import B3dmLoader from "./B3dmLoader.js"; /** diff --git a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js index 40426fd93090..48c7192a75ce 100644 --- a/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js +++ b/Specs/Scene/ModelExperimental/B3dmLoaderSpec.js @@ -4,7 +4,6 @@ import { Cartesian3, GltfLoader, Matrix4, - MetadataClass, Resource, ResourceCache, } from "../../../Source/Cesium.js"; From f692e9ea2b3e253dbbf7e4f202a60c7550f5b829 Mon Sep 17 00:00:00 2001 From: Sanjeet Suhag Date: Thu, 21 Oct 2021 18:52:21 -0400 Subject: [PATCH 31/31] Adds back name --- Source/Scene/ModelExperimental/B3dmLoader.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Scene/ModelExperimental/B3dmLoader.js b/Source/Scene/ModelExperimental/B3dmLoader.js index 89f3bcdaa09d..fb8763a617c3 100644 --- a/Source/Scene/ModelExperimental/B3dmLoader.js +++ b/Source/Scene/ModelExperimental/B3dmLoader.js @@ -9,6 +9,7 @@ import defined from "../../Core/defined.js"; import FeatureMetadata from "../FeatureMetadata.js"; import GltfLoader from "../GltfLoader.js"; import Matrix4 from "../../Core/Matrix4.js"; +import MetadataClass from "../MetadataClass.js"; import ModelComponents from "../ModelComponents.js"; import ModelExperimentalUtility from "./ModelExperimentalUtility.js"; import parseBatchTable from "../parseBatchTable.js"; @@ -291,6 +292,7 @@ function createFeatureMetadata(loader, components) { } else { // If batch table is not defined, create a property table without any properties. var emptyPropertyTable = new PropertyTable({ + name: MetadataClass.BATCH_TABLE_CLASS_NAME, count: batchLength, }); featureMetadata = new FeatureMetadata({