From eb4ec2036bf267d064dd68be1afcff7eff55046b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 17 Sep 2024 17:01:41 +0200 Subject: [PATCH 01/38] First, partial support of GPM classes --- packages/engine/Source/Scene/GltfGpmLoader.js | 469 ++++++++++++++++++ .../Model/Extensions/Gpm/AnchorPointDirect.js | 50 ++ .../Extensions/Gpm/AnchorPointIndirect.js | 66 +++ .../Model/Extensions/Gpm/CorrelationGroup.js | 67 +++ .../Model/Extensions/Gpm/GltfGpmLocal.js | 145 ++++++ .../Extensions/Gpm/MeshPrimitiveGpmLocal.js | 27 + .../Scene/Model/Extensions/Gpm/PpeMetadata.js | 67 +++ .../Scene/Model/Extensions/Gpm/PpeSource.js | 72 +++ .../Scene/Model/Extensions/Gpm/PpeTexture.js | 115 +++++ .../Scene/Model/Extensions/Gpm/Spdcf.js | 87 ++++ .../Scene/Model/Extensions/Gpm/StorageType.js | 27 + 11 files changed, 1192 insertions(+) create mode 100644 packages/engine/Source/Scene/GltfGpmLoader.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js create mode 100644 packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js diff --git a/packages/engine/Source/Scene/GltfGpmLoader.js b/packages/engine/Source/Scene/GltfGpmLoader.js new file mode 100644 index 00000000000..b3463abb805 --- /dev/null +++ b/packages/engine/Source/Scene/GltfGpmLoader.js @@ -0,0 +1,469 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import defined from "../Core/defined.js"; +import ResourceCache from "./ResourceCache.js"; +import ResourceLoader from "./ResourceLoader.js"; +import ResourceLoaderState from "./ResourceLoaderState.js"; +import PropertyTexture from "./PropertyTexture.js"; +import StructuralMetadata from "./StructuralMetadata.js"; +import MetadataSchema from "./MetadataSchema.js"; + +/** + * Loads glTF NGA_gpm_local + *

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

+ * Implementation note: This is an experimental implementation. It is based + * on a GltfStructuralMetadataLoader, by removing everything that is not + * related to property textures, and translating the "ppeTextures" of + * the NGA_gpm_local extension into property textures. These will be + * returned as part of a `StructuralMetadata` object, which may override + * any `StructuralMetadata` that was read directly from the glTF. + * + * @alias GltfGpmLoader + * @constructor + * @augments ResourceLoader + * + * @param {object} options Object with the following properties: + * @param {object} options.gltf The glTF JSON. + * @param {string} [options.extension] The NGA_gpm_local extension object. + * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. + * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. + * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats. + * @param {FrameState} options.frameState The frame state. + * @param {string} [options.cacheKey] The cache key of the resource. + * @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. + * + * @private + * @experimental This feature is subject to change without Cesium's standard deprecation policy. + */ +function GltfGpmLoader(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + const gltf = options.gltf; + const extension = options.extension; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const supportedImageFormats = options.supportedImageFormats; + const frameState = options.frameState; + const cacheKey = options.cacheKey; + const asynchronous = defaultValue(options.asynchronous, true); + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.object("options.extension", extension); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); + Check.typeOf.object("options.frameState", frameState); + //>>includeEnd('debug'); + + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._extension = extension; + this._supportedImageFormats = supportedImageFormats; + this._frameState = frameState; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._textureLoaders = []; + this._textureIds = []; + this._structuralMetadata = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; +} + +if (defined(Object.create)) { + GltfGpmLoader.prototype = Object.create(ResourceLoader.prototype); + GltfGpmLoader.prototype.constructor = GltfGpmLoader; +} + +Object.defineProperties(GltfGpmLoader.prototype, { + /** + * The cache key of the resource. + * + * @memberof GltfGpmLoader.prototype + * + * @type {string} + * @readonly + * @private + */ + cacheKey: { + get: function () { + return this._cacheKey; + }, + }, + /** + * The parsed structural metadata + * + * @memberof GltfGpmLoader.prototype + * + * @type {StructuralMetadata} + * @readonly + * @private + */ + structuralMetadata: { + get: function () { + return this._structuralMetadata; + }, + }, +}); + +async function loadResources(loader) { + try { + const texturesPromise = loadTextures(loader); + await texturesPromise; + + if (loader.isDestroyed()) { + return; + } + + loader._gltf = undefined; // No longer need to hold onto the glTF + + loader._state = ResourceLoaderState.LOADED; + return loader; + } catch (error) { + if (loader.isDestroyed()) { + return; + } + + loader.unload(); + loader._state = ResourceLoaderState.FAILED; + const errorMessage = "Failed to load GPM data"; + throw loader.getError(errorMessage, error); + } +} + +/** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ +GltfGpmLoader.prototype.load = function () { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; +}; + +function gatherUsedTextureIds(gpmExtension) { + // Gather the used textures + const textureIds = {}; + const ppeTextures = gpmExtension.ppeTextures; + if (defined(ppeTextures)) { + for (let i = 0; i < ppeTextures.length; i++) { + const ppeTexture = ppeTextures[i]; + // The texture is a valid textureInfo. + textureIds[ppeTexture.index] = ppeTexture; + } + } + return textureIds; +} + +function loadTextures(gpmLoader) { + let textureIds; + if (defined(gpmLoader._extension)) { + textureIds = gatherUsedTextureIds(gpmLoader._extension); + } + + const gltf = gpmLoader._gltf; + const gltfResource = gpmLoader._gltfResource; + const baseResource = gpmLoader._baseResource; + const supportedImageFormats = gpmLoader._supportedImageFormats; + const frameState = gpmLoader._frameState; + const asynchronous = gpmLoader._asynchronous; + + // Load the textures + const texturePromises = []; + for (const textureId in textureIds) { + if (textureIds.hasOwnProperty(textureId)) { + const textureLoader = ResourceCache.getTextureLoader({ + gltf: gltf, + textureInfo: textureIds[textureId], + gltfResource: gltfResource, + baseResource: baseResource, + supportedImageFormats: supportedImageFormats, + frameState: frameState, + asynchronous: asynchronous, + }); + gpmLoader._textureLoaders.push(textureLoader); + gpmLoader._textureIds.push(textureId); + texturePromises.push(textureLoader.load()); + } + } + + return Promise.all(texturePromises); +} + +/** + * A static mapping from PPE texture property identifier keys + * to `MetadataSchema` instances. This is used to create each + * schema (with a certain structure) only ONCE in + * obtainPpeTexturesMetadataSchema + * + * @private + */ +GltfGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); + +/** + * Create the JSON description of a metadata class that treats + * the given PPE texture as a property texture property(!). + * + * @param {any} ppeTexture - The PPE texture + * @param {number} index - The index of the texture in the extension + * @returns The class JSON + */ +GltfGpmLoader.prototype.createPpeTextureClassJson = function ( + ppeTexture, + index +) { + const traits = ppeTexture.traits; + const ppePropertyName = traits.source; + + // XXX_UNCERTAINTY: The ppeTexture will have a structure + // like this: + // + //"ppeTextures" : [ + // { + // "traits" : { + // "source" : "SIGZ", + // "min" : 0.0, + // "max" : 16.0 + // }, + // "index" : 2, + // "noData" : 255, + // "offset" : 0.0, + // "scale" : 0.06274509803921569, + // "texCoord" : 1 + // }, + // + // Note that in GPM 1.2d, the min/max should be part of the + // texture, but in the actual data (GPM 1.2i), they are in + // the traits (ppeMetadata). And there, they/ seem to denote + // the min/max values that actually appear in the texture. + // So they are integrated into the 'scale' factor here: + const min = traits.min ?? 0; + const max = traits.max ?? 255; + const minMaxScale = 255.0 / (max - min); + const offset = ppeTexture.offset; + const scale = ppeTexture.scale * minMaxScale; + const classJson = { + name: `PPE texture class ${index}`, + properties: { + [ppePropertyName]: { + name: "PPE", + type: "SCALAR", + componentType: "UINT8", + normalized: true, + offset: offset, + scale: scale, + }, + }, + }; + return classJson; +}; + +/** + * Returns the `MetadataSchema` for the PPE textures in this instance. + * + * This method will return a (statically/globally) cached metadata + * schema that reflects the structure of the PPE textures in this + * instance, creating and caching it if necessary. + * + * For details on the cache key, see `collectPpeTexturePropertyIdentifiers` + * + * @returns The `MetadataSchema` + */ +GltfGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function () { + const ppeTexturePropertyIdentifiers = this.collectPpeTexturePropertyIdentifiers(); + const key = ppeTexturePropertyIdentifiers.toString(); + let ppeTexturesMetadataSchema = GltfGpmLoader.ppeTexturesMetadataSchemaCache.get( + key + ); + if (defined(ppeTexturesMetadataSchema)) { + // XXX_UNCERTAINTY Debug log + //console.log(`Using cached schema for GPM PPE textures with key ${key}`); + return ppeTexturesMetadataSchema; + } + + // XXX_UNCERTAINTY Debug log - if caching works, this should be printed only ONCE! + console.log(`Creating schema for GPM PPE textures with key ${key}`); + const schemaId = `PPE_TEXTURE_SCHEMA_${GltfGpmLoader.ppeTexturesMetadataSchemaCache.size}`; + const ppeTexturesMetadataSchemaJson = { + id: schemaId, + classes: {}, + }; + + const extension = this._extension; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const classJson = this.createPpeTextureClassJson(ppeTexture, i); + ppeTexturesMetadataSchemaJson.classes[classId] = classJson; + } + } + + ppeTexturesMetadataSchema = MetadataSchema.fromJson( + ppeTexturesMetadataSchemaJson + ); + GltfGpmLoader.ppeTexturesMetadataSchemaCache.set( + key, + ppeTexturesMetadataSchema + ); + return ppeTexturesMetadataSchema; +}; + +/** + * Creates an array of strings that serve as identifiers for PPE textures. + * + * Each glTF may define multiple `ppeTexture` objects within the + * `NGA_gpm_local` extensions. Each of these textures corresponds + * to one 'property texture property(!)' in a metadata schema. + * + * This method will create an array where each element is a (JSON) + * string representation of the parts of a GPM PPE texture definition + * that are relevant for distinguishing two PPE textures in terms + * of their structure within a `StructuralMetadata`. + * + * @returns The identifiers + */ +GltfGpmLoader.prototype.collectPpeTexturePropertyIdentifiers = function () { + const extension = this._extension; + const ppeTexturePropertyIdentifiers = []; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + // The following will create an identifier that can be used + // to define two PPE textures as "representing the same + // property texture property" within a structural metadata + // schema. + const classJson = this.createPpeTextureClassJson(ppeTexture, i); + const ppeTexturePropertyIdentifier = JSON.stringify(classJson); + ppeTexturePropertyIdentifiers.push(ppeTexturePropertyIdentifier); + } + } + return ppeTexturePropertyIdentifiers; +}; + +/** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ +GltfGpmLoader.prototype.process = function (frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state !== ResourceLoaderState.LOADED) { + return false; + } + + const textureLoaders = this._textureLoaders; + const textureLoadersLength = textureLoaders.length; + let ready = true; + for (let i = 0; i < textureLoadersLength; ++i) { + const textureLoader = textureLoaders[i]; + const textureReady = textureLoader.process(frameState); + ready = ready && textureReady; + } + + if (!ready) { + return false; + } + + const textures = {}; + for (let i = 0; i < this._textureIds.length; ++i) { + const textureId = this._textureIds[i]; + const textureLoader = textureLoaders[i]; + if (!textureLoader.isDestroyed()) { + textures[textureId] = textureLoader.texture; + } + } + + const ppeTexturesMetadataSchema = this.obtainPpeTexturesMetadataSchema(); + const extension = this._extension; + const propertyTextures = []; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const traits = ppeTexture.traits; + const ppePropertyName = traits.source; + const metadataClass = ppeTexturesMetadataSchema.classes[classId]; + + // XXX_UNCERTAINTY Debug log + //console.log( + // `Creating property texture with class ${classId} and property ${ppePropertyName}` + //); + + const ppeTextureAsPropertyTexture = { + class: classId, + properties: { + [ppePropertyName]: { + index: ppeTexture.index, + texCoord: ppeTexture.texCoord, + }, + }, + }; + propertyTextures.push( + new PropertyTexture({ + id: i, + name: ppeTexture.name, + propertyTexture: ppeTextureAsPropertyTexture, + class: metadataClass, + textures: textures, + }) + ); + } + } + + const structuralMetadata = new StructuralMetadata({ + schema: ppeTexturesMetadataSchema, + propertyTables: [], + propertyTextures: propertyTextures, + propertyAttributes: [], + statistics: extension.statistics, + extras: extension.extras, + extensions: extension.extensions, + }); + this._structuralMetadata = structuralMetadata; + + this._state = ResourceLoaderState.READY; + return true; +}; + +function unloadTextures(gpmLoader) { + const textureLoaders = gpmLoader._textureLoaders; + const textureLoadersLength = textureLoaders.length; + for (let i = 0; i < textureLoadersLength; ++i) { + ResourceCache.unload(textureLoaders[i]); + } + gpmLoader._textureLoaders.length = 0; + gpmLoader._textureIds.length = 0; +} + +/** + * Unloads the resource. + * @private + */ +GltfGpmLoader.prototype.unload = function () { + unloadTextures(this); + + if (defined(this._schemaLoader)) { + ResourceCache.unload(this._schemaLoader); + } + this._schemaLoader = undefined; + + this._structuralMetadata = undefined; +}; + +export default GltfGpmLoader; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js new file mode 100644 index 00000000000..79e71895214 --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js @@ -0,0 +1,50 @@ +import Check from "../Core/Check.js"; + +/** + * Metadata for one stored anchor point using direct storage + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function AnchorPointDirect(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.position", options.property); + Check.typeOf.object("options.adjustmentParams", options.adjustmentParams); + //>>includeEnd('debug'); + + this._position = options.position; + this._adjustmentParams = options.adjustmentParams; +} + +Object.defineProperties(AnchorPointDirect.prototype, { + /** + * Anchor point geographic coordinates in meters as X/Easting, Y/Northing, Z/HAE + * + * @memberof AnchorPointDirect.prototype + * @type {Cartesian3} + * @readonly + * @private + */ + position: { + get: function () { + return this._position; + }, + }, + + /** + * The delta-x delta-y delta-z adjustment values in meters per anchor + * point. + * + * @memberof AnchorPointDirect.prototype + * @type {Cartesian3} + * @readonly + * @private + */ + adjustmentParams: { + get: function () { + return this._adjustmentParams; + }, + }, +}); + +export default AnchorPointDirect; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js new file mode 100644 index 00000000000..c70996f3348 --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js @@ -0,0 +1,66 @@ +import Check from "../Core/Check.js"; + +/** + * Metadata for one stored anchor point. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function AnchorPointIndirect(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.position", options.property); + Check.typeOf.object("options.adjustmentParams", options.adjustmentParams); + Check.typeOf.object("options.covarianceMatrix", options.covarianceMatrix); + //>>includeEnd('debug'); + + this._position = options.position; + this._adjustmentParams = options.adjustmentParams; + this._covarianceMatrix = options.covarianceMatrix; +} + +Object.defineProperties(AnchorPointIndirect.prototype, { + /** + * Anchor point geographic coordinates in meters as X/Easting, Y/Northing, Z/HAE + * + * @memberof AnchorPointIndirect.prototype + * @type {Cartesian3} + * @readonly + * @private + */ + position: { + get: function () { + return this._position; + }, + }, + + /** + * The delta-x delta-y delta-z adjustment values in meters per anchor + * point. + * + * @memberof AnchorPointIndirect.prototype + * @type {Cartesian3} + * @readonly + * @private + */ + adjustmentParams: { + get: function () { + return this._adjustmentParams; + }, + }, + + /** + * The 3x3 covariance matrix. + * + * @memberof AnchorPointIndirect.prototype + * @type {Matrix3} + * @readonly + * @private + */ + covarianceMatrix: { + get: function () { + return this._covarianceMatrix; + }, + }, +}); + +export default AnchorPointIndirect; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js new file mode 100644 index 00000000000..e535ea6a43a --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js @@ -0,0 +1,67 @@ +import Check from "../Core/Check.js"; + +/** + * Metadata identifying parameters using same correlation modeling and + * associated correlation parameters. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function CorrelationGroup(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.groupFlags", options.groupFlags); + Check.typeOf.object("options.rotationThetas", options.rotationThetas); + Check.typeOf.object("options.params", options.params); + //>>includeEnd('debug'); + + this._groupFlags = options.groupFlags; + this._rotationThetas = options.rotationThetas; + this._params = options.params; +} + +Object.defineProperties(CorrelationGroup.prototype, { + /** + * Array of 3 booleans indicating if parameters delta-x delta-y delta-z + * used in the correlation group + * + * @memberof CorrelationGroup.prototype + * @type {boolean[]} + * @readonly + * @private + */ + groupFlags: { + get: function () { + return this._groupFlags; + }, + }, + + /** + * Rotations in milliradians about X, Y, Z axes, respectively + * + * @memberof CorrelationGroup.prototype + * @type {Cartesian3} + * @readonly + * @private + */ + rotationThetas: { + get: function () { + return this._rotationThetas; + }, + }, + + /** + * Array of 3 sets of SPDCF parameters, for the U, V, W directions, respectively + * + * @memberof CorrelationGroup.prototype + * @type {Spdcf[]} + * @readonly + * @private + */ + params: { + get: function () { + return this._params; + }, + }, +}); + +export default CorrelationGroup; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js new file mode 100644 index 00000000000..016c27bf81c --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -0,0 +1,145 @@ +import defined from "../Core/defined.js"; +import Check from "../Core/Check.js"; +import StorageType from "./StorageType.js"; +import RuntimeError from "../Core/RuntimeError.js"; + +/** + * The GPM metadata for a Ground-Space Indirect implementation stored + * locally (i.e. a tile and/or leaf node). + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function GltfGpmLocal(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.storageType", options.storageType); + //>>includeEnd('debug'); + + this._storageType = options.storageType; + this._anchorPointsIndirect = options.anchorPointsIndirect; + this._anchorPointsDirect = options.anchorPointsDirect; + this._intraTileCorrelationGroups = options.intraTileCorrelationGroups; + this._covarianceDirect = options.covarianceDirect; + + //>>includeStart('debug', pragmas.debug); + if (this.storageType === StorageType.Indirect) { + if (!defined(this.anchorPointsIndirect)) { + throw new RuntimeError( + "The anchorPointsIndirect are required for 'Indirect' storage" + ); + } + if (!defined(this.intraTileCorrelationGroups)) { + throw new RuntimeError( + "The intraTileCorrelationGroups are required for 'Indirect' storage" + ); + } + if (defined(this.anchorPointsDirect)) { + throw new RuntimeError( + "The anchorPointsDirect must be omitted for 'Indirect' storage" + ); + } + if (defined(this.covarianceDirect)) { + throw new RuntimeError( + "The covarianceDirect must be omitted for 'Indirect' storage" + ); + } + } else { + // Direct storage + if (!defined(this.anchorPointsDirect)) { + throw new RuntimeError( + "The anchorPointsDirect are required for 'Direct' storage" + ); + } + if (!defined(this.covarianceDirect)) { + throw new RuntimeError( + "The covarianceDirect is required for 'Direct' storage" + ); + } + if (defined(this.anchorPointsIndirect)) { + throw new RuntimeError( + "The anchorPointsIndirect must be omitted for 'Direct' storage" + ); + } + if (defined(this.intraTileCorrelationGroups)) { + throw new RuntimeError( + "The intraTileCorrelationGroups must be omitted for 'Direct' storage" + ); + } + } + //>>includeEnd('debug'); +} + +Object.defineProperties(GltfGpmLocal.prototype, { + /** + * Specifies if covariance storage is indirect or direct. + * + * @memberof GltfGpmLocal.prototype + * @type {StorageType} + * @readonly + * @private + */ + storageType: { + get: function () { + return this._storageType; + }, + }, + + /** + * Array of stored indirect anchor points + * + * @memberof GltfGpmLocal.prototype + * @type {AnchorPointIndirect[]} + * @readonly + * @private + */ + anchorPointsIndirect: { + get: function () { + return this._anchorPointsIndirect; + }, + }, + + /** + * Array of stored direct anchor points + * + * @memberof GltfGpmLocal.prototype + * @type {AnchorPointDirect[]} + * @readonly + * @private + */ + anchorPointsDirect: { + get: function () { + return this._anchorPointsDirect; + }, + }, + + /** + * Metadata identifying parameters using same correlation modeling and + * associated correlation parameters + * + * @memberof GltfGpmLocal.prototype + * @type {CorrelationGroup[]} + * @readonly + * @private + */ + intraTileCorrelationGroups: { + get: function () { + return this._intraTileCorrelationGroups; + }, + }, + + /** + * The full covariance of anchor point parameters + * + * @memberof GltfGpmLocal.prototype + * @type {Matrix3} + * @readonly + * @private + */ + covarianceDirect: { + get: function () { + return this._covarianceDirect; + }, + }, +}); + +export default GltfGpmLocal; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js new file mode 100644 index 00000000000..1ca12213774 --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js @@ -0,0 +1,27 @@ +/** + * Local Generic Point-cloud Model information about a glTF primitive. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function MeshPrimitiveGpmLocal(options) { + this._ppeTextures = options.ppeTextures; +} + +Object.defineProperties(MeshPrimitiveGpmLocal.prototype, { + /** + * An array of ppe textures. + * + * @memberof MeshPrimitiveGpmLocal.prototype + * @type {PpeTexture[]|undefined} + * @readonly + * @private + */ + ppeTextures: { + get: function () { + return this._ppeTextures; + }, + }, +}); + +export default MeshPrimitiveGpmLocal; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js new file mode 100644 index 00000000000..26f0d152284 --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -0,0 +1,67 @@ +import Check from "../Core/Check.js"; + +/** + * Metadata related to the stored PPE data. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function PpeMetadata(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.source", options.source); + //>>includeEnd('debug'); + + this._min = options.min; + this._max = options.max; + this._source = options.source; +} + +Object.defineProperties(PpeMetadata.prototype, { + /** + * Minimum allowed value for the property. This is the minimum of all + * values after the transforms based on the offset and scale properties + * have been applied. + * + * @memberof PpeMetadata.prototype + * @type {number|undefined} + * @readonly + * @private + */ + min: { + get: function () { + return this._min; + }, + }, + + /** + * Maximum allowed value for the property. This is the maximum of all + * values after the transforms based on the offset and scale properties + * have been applied. + * + * @memberof PpeMetadata.prototype + * @type {number|undefined} + * @readonly + * @private + */ + max: { + get: function () { + return this._max; + }, + }, + + /** + * Possible error source contents + * + * @memberof PpeMetadata.prototype + * @type {PpeSource} + * @readonly + * @private + */ + source: { + get: function () { + return this._source; + }, + }, +}); + +export default PpeMetadata; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js new file mode 100644 index 00000000000..d44acabe4e1 --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js @@ -0,0 +1,72 @@ +/** + * An enum of per-point error sources + * + * @enum {string} + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +const PpeSource = { + /** + * The PPE standard deviation of error in the x dimension of the MCS (sigma x). Value will be squared + * and used to populate the (1,1) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + SIGX: "SIGX", + + /** + * The PPE standard deviation of error in the y dimension of the MCS (sigma y). Value will be squared + * and used to populate the (2,2) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + SIGY: "SIGY", + + /** + * The PPE standard deviation of error in the z dimension of the MCS (sigma z). Value will be squared + * and used to populate the (3,3) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + SIGZ: "SIGZ", + + /** + * The PPE variance of error in the x dimension of the MCS (sigma x2). Value will be used to populate + * the (1,1) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + VARX: "VARX", + + /** + * The PPE variance of error in the y dimension of the MCS (sigma y2). Value will be used to populate + * the (2,2) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + VARY: "VARY", + + /** + * The PPE variance of error in the z dimension of the MCS (sigma z2). Value will be used to populate + * the (3,3) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + VARZ: "VARZ", + + /** + * The PPE radial error in the horizontal dimension (x-y) of the MCS (sigma radial) . Value will be squared + * and used to populate the (1,1) and (2,2) element in the PPE covariance matrix. + * + * @type {string} + * @constant + */ + SIGR: "VARZ", +}; + +export default Object.freeze(PpeSource); diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js new file mode 100644 index 00000000000..07d2fb5b6be --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -0,0 +1,115 @@ +import Check from "../Core/Check.js"; + +/** + * PPE (Per-Point Error) texture in `NGA_gpm_local`. + * + * This is a valid glTF `TextureInfo` object (with a required `index` + * and an optional `texCoord)`, with additional properties that + * describe the structure of the metdata that is stored in the texture. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function PpeTexture(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.traits", options.traits); + Check.typeOf.object("options.index", options.index); + //>>includeEnd('debug'); + + this._traits = options.traits; + this._noData = options.noData; + this._offset = options.offset; + this._scale = options.scale; + this._index = options.index; + this._texCoord = options.texCoord; +} + +Object.defineProperties(PpeTexture.prototype, { + /** + * The data contained here applies to this node and corresponding + * texture. + * + * @memberof PpeTexture.prototype + * @type {PpeMetadata} + * @readonly + * @private + */ + traits: { + get: function () { + return this._traits; + }, + }, + + /** + * A value to represent missing data - also known as a sentinel value - + * wherever it appears. + * + * @memberof PpeTexture.prototype + * @type {number|undefined} + * @readonly + * @private + */ + noData: { + get: function () { + return this._noData; + }, + }, + + /** + * An offset to apply to property values. + * + * @memberof PpeTexture.prototype + * @type {number|undefined} + * @readonly + * @private + */ + offset: { + get: function () { + return this._offset; + }, + }, + + /** + * An scale to apply to property values. + * + * @memberof PpeTexture.prototype + * @type {number|undefined} + * @readonly + * @private + */ + scale: { + get: function () { + return this._scale; + }, + }, + + /** + * The index of the texture + * + * @memberof PpeTexture.prototype + * @type {number} + * @readonly + * @private + */ + index: { + get: function () { + return this._index; + }, + }, + + /** + * The set index of texture's TEXCOORD attribute used for texture coordinate mapping. + * + * @memberof PpeTexture.prototype + * @type {number|undefined} + * @readonly + * @private + */ + texCoord: { + get: function () { + return this._texCoord; + }, + }, +}); + +export default PpeTexture; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js new file mode 100644 index 00000000000..548752d533e --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js @@ -0,0 +1,87 @@ +import Check from "../Core/Check.js"; + +/** + * Variables for a Strictly Positive-Definite Correlation Function. + * + * Parameters (A, alpha, beta, T) used to describe the correlation decrease + * between points as a function of delta time. + * + * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +function Spdcf(options) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThan("options.A", options.A, 0.0); + Check.typeOf.number.lessThanOrEquals("options.A", options.A, 1.0); + Check.typeOf.number.greaterThanOrEquals("options.alpha", options.alpha, 0.0); + Check.typeOf.number.lessThan("options.alpha", options.alpha, 1.0); + Check.typeOf.number.greaterThanOrEquals("options.beta", options.beta, 0.0); + Check.typeOf.number.lessThanOrEquals("options.beta", options.beta, 10.0); + Check.typeOf.number.greaterThan("options.T", options.T, 0.0); + //>>includeEnd('debug'); + + this._A = options.A; + this._alpha = options.alpha; + this._beta = options.beta; + this._T = options.T; +} + +Object.defineProperties(Spdcf.prototype, { + /** + * In (0, 1] + * + * @memberof Spdcf.prototype + * @type {number} + * @readonly + * @private + */ + A: { + get: function () { + return this._A; + }, + }, + + /** + * In [0, 1) + * + * @memberof Spdcf.prototype + * @type {number} + * @readonly + * @private + */ + alpha: { + get: function () { + return this._alpha; + }, + }, + + /** + * In [0, 10] + * + * @memberof Spdcf.prototype + * @type {number} + * @readonly + * @private + */ + beta: { + get: function () { + return this._beta; + }, + }, + + /** + * In (0, +inf) + * + * @memberof Spdcf.prototype + * @type {number} + * @readonly + * @private + */ + T: { + get: function () { + return this._T; + }, + }, +}); + +export default Spdcf; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js new file mode 100644 index 00000000000..f706c32b63e --- /dev/null +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js @@ -0,0 +1,27 @@ +/** + * An enum of storage types for covariance information + * + * @enum {string} + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + */ +const StorageType = { + /** + * Store the full error covariance of the anchor points, to include the cross-covariance terms + * + * @type {string} + * @constant + */ + Direct: "Direct", + + /** + * A full covariance matrix is stored for each of the anchor points. However, in this case the + * cross-covariance terms are not directly stored, but can be computed by a set of spatial + * correlation function parameters which are stored in the metadata. + * + * @type {string} + * @constant + */ + Indirect: "Indirect", +}; + +export default Object.freeze(StorageType); From d7f1c9195afb7016cd5e0c290f7dc740195903d9 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 18 Sep 2024 18:30:57 +0200 Subject: [PATCH 02/38] Fix import and usage of 'Check' --- .../Scene/Model/Extensions/Gpm/AnchorPointDirect.js | 4 ++-- .../Scene/Model/Extensions/Gpm/AnchorPointIndirect.js | 4 ++-- .../Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js | 2 +- .../Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js | 8 ++++---- .../Source/Scene/Model/Extensions/Gpm/PpeMetadata.js | 2 +- .../Source/Scene/Model/Extensions/Gpm/PpeTexture.js | 2 +- .../engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js index 79e71895214..903330dada7 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * Metadata for one stored anchor point using direct storage @@ -8,7 +8,7 @@ import Check from "../Core/Check.js"; */ function AnchorPointDirect(options) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.position", options.property); + Check.typeOf.object("options.position", options.position); Check.typeOf.object("options.adjustmentParams", options.adjustmentParams); //>>includeEnd('debug'); diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js index c70996f3348..f0102db3294 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * Metadata for one stored anchor point. @@ -8,7 +8,7 @@ import Check from "../Core/Check.js"; */ function AnchorPointIndirect(options) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.position", options.property); + Check.typeOf.object("options.position", options.position); Check.typeOf.object("options.adjustmentParams", options.adjustmentParams); Check.typeOf.object("options.covarianceMatrix", options.covarianceMatrix); //>>includeEnd('debug'); diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js index e535ea6a43a..cc82812aa60 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * Metadata identifying parameters using same correlation modeling and diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js index 016c27bf81c..f6b9e4aab5d 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -1,7 +1,7 @@ -import defined from "../Core/defined.js"; -import Check from "../Core/Check.js"; +import defined from "../../../../Core/defined.js"; +import Check from "../../../../Core/Check.js"; +import RuntimeError from "../../../../Core/RuntimeError.js"; import StorageType from "./StorageType.js"; -import RuntimeError from "../Core/RuntimeError.js"; /** * The GPM metadata for a Ground-Space Indirect implementation stored @@ -12,7 +12,7 @@ import RuntimeError from "../Core/RuntimeError.js"; */ function GltfGpmLocal(options) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.storageType", options.storageType); + Check.typeOf.string("options.storageType", options.storageType); //>>includeEnd('debug'); this._storageType = options.storageType; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index 26f0d152284..de255f5b6dd 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * Metadata related to the stored PPE data. diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 07d2fb5b6be..5214a6e34ac 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * PPE (Per-Point Error) texture in `NGA_gpm_local`. diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js index 548752d533e..7687ed32ed8 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js @@ -1,4 +1,4 @@ -import Check from "../Core/Check.js"; +import Check from "../../../../Core/Check.js"; /** * Variables for a Strictly Positive-Definite Correlation Function. From e09cb04035f4e654249cccab80e582f5aac0f222 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 18 Sep 2024 18:32:04 +0200 Subject: [PATCH 03/38] Start wiring in GPM loader --- packages/engine/Source/Scene/GltfGpmLoader.js | 466 +++++------------- packages/engine/Source/Scene/GltfLoader.js | 105 ++++ .../Scene/GltfMeshPrimitiveGpmLoader.js | 465 +++++++++++++++++ packages/engine/Source/Scene/Model/Model.js | 18 + .../Source/Scene/Model/Model3DTileContent.js | 10 +- .../engine/Source/Scene/ModelComponents.js | 6 + 6 files changed, 737 insertions(+), 333 deletions(-) create mode 100644 packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js diff --git a/packages/engine/Source/Scene/GltfGpmLoader.js b/packages/engine/Source/Scene/GltfGpmLoader.js index b3463abb805..4fe1cf50c46 100644 --- a/packages/engine/Source/Scene/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/GltfGpmLoader.js @@ -1,24 +1,23 @@ +import Cartesian3 from "../Core/Cartesian3.js"; import Check from "../Core/Check.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; -import ResourceCache from "./ResourceCache.js"; +import Matrix3 from "../Core/Matrix3.js"; +import AnchorPointDirect from "./Model/Extensions/Gpm/AnchorPointDirect.js"; +import AnchorPointIndirect from "./Model/Extensions/Gpm/AnchorPointIndirect.js"; +import CorrelationGroup from "./Model/Extensions/Gpm/CorrelationGroup.js"; +import GltfGpmLocal from "./Model/Extensions/Gpm/GltfGpmLocal.js"; +import Spdcf from "./Model/Extensions/Gpm/Spdcf.js"; +import StorageType from "./Model/Extensions/Gpm/StorageType.js"; import ResourceLoader from "./ResourceLoader.js"; import ResourceLoaderState from "./ResourceLoaderState.js"; -import PropertyTexture from "./PropertyTexture.js"; -import StructuralMetadata from "./StructuralMetadata.js"; -import MetadataSchema from "./MetadataSchema.js"; /** - * Loads glTF NGA_gpm_local + * Loads glTF NGA_gpm_local from the root of a glTF object *

* Implements the {@link ResourceLoader} interface. *

- * Implementation note: This is an experimental implementation. It is based - * on a GltfStructuralMetadataLoader, by removing everything that is not - * related to property textures, and translating the "ppeTextures" of - * the NGA_gpm_local extension into property textures. These will be - * returned as part of a `StructuralMetadata` object, which may override - * any `StructuralMetadata` that was read directly from the glTF. + * Implementation note: This is an experimental implementation. * * @alias GltfGpmLoader * @constructor @@ -29,8 +28,6 @@ import MetadataSchema from "./MetadataSchema.js"; * @param {string} [options.extension] The NGA_gpm_local extension object. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. - * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats. - * @param {FrameState} options.frameState The frame state. * @param {string} [options.cacheKey] The cache key of the resource. * @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. * @@ -43,8 +40,6 @@ function GltfGpmLoader(options) { const extension = options.extension; const gltfResource = options.gltfResource; const baseResource = options.baseResource; - const supportedImageFormats = options.supportedImageFormats; - const frameState = options.frameState; const cacheKey = options.cacheKey; const asynchronous = defaultValue(options.asynchronous, true); @@ -53,22 +48,19 @@ function GltfGpmLoader(options) { Check.typeOf.object("options.extension", extension); Check.typeOf.object("options.gltfResource", gltfResource); Check.typeOf.object("options.baseResource", baseResource); - Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); - Check.typeOf.object("options.frameState", frameState); //>>includeEnd('debug'); this._gltfResource = gltfResource; this._baseResource = baseResource; this._gltf = gltf; this._extension = extension; - this._supportedImageFormats = supportedImageFormats; - this._frameState = frameState; this._cacheKey = cacheKey; this._asynchronous = asynchronous; - this._textureLoaders = []; - this._textureIds = []; - this._structuralMetadata = undefined; - this._state = ResourceLoaderState.UNLOADED; + this._gltfGpmLocal = undefined; + + // Immediately go into the "LOADED" state, since there + // are no resources to wait for + this._state = ResourceLoaderState.LOADED; this._promise = undefined; } @@ -93,46 +85,21 @@ Object.defineProperties(GltfGpmLoader.prototype, { }, }, /** - * The parsed structural metadata + * The parsed GltfGpmLocal object * * @memberof GltfGpmLoader.prototype * - * @type {StructuralMetadata} + * @type {GltfGpmLocal} * @readonly * @private */ - structuralMetadata: { + gltfGpmLocal: { get: function () { - return this._structuralMetadata; + return this._gltfGpmLocal; }, }, }); -async function loadResources(loader) { - try { - const texturesPromise = loadTextures(loader); - await texturesPromise; - - if (loader.isDestroyed()) { - return; - } - - loader._gltf = undefined; // No longer need to hold onto the glTF - - loader._state = ResourceLoaderState.LOADED; - return loader; - } catch (error) { - if (loader.isDestroyed()) { - return; - } - - loader.unload(); - loader._state = ResourceLoaderState.FAILED; - const errorMessage = "Failed to load GPM data"; - throw loader.getError(errorMessage, error); - } -} - /** * Loads the resource. * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. @@ -142,211 +109,32 @@ GltfGpmLoader.prototype.load = function () { if (defined(this._promise)) { return this._promise; } - - this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); + this._promise = Promise.resolve(this); return this._promise; }; -function gatherUsedTextureIds(gpmExtension) { - // Gather the used textures - const textureIds = {}; - const ppeTextures = gpmExtension.ppeTextures; - if (defined(ppeTextures)) { - for (let i = 0; i < ppeTextures.length; i++) { - const ppeTexture = ppeTextures[i]; - // The texture is a valid textureInfo. - textureIds[ppeTexture.index] = ppeTexture; - } - } - return textureIds; -} - -function loadTextures(gpmLoader) { - let textureIds; - if (defined(gpmLoader._extension)) { - textureIds = gatherUsedTextureIds(gpmLoader._extension); - } - - const gltf = gpmLoader._gltf; - const gltfResource = gpmLoader._gltfResource; - const baseResource = gpmLoader._baseResource; - const supportedImageFormats = gpmLoader._supportedImageFormats; - const frameState = gpmLoader._frameState; - const asynchronous = gpmLoader._asynchronous; - - // Load the textures - const texturePromises = []; - for (const textureId in textureIds) { - if (textureIds.hasOwnProperty(textureId)) { - const textureLoader = ResourceCache.getTextureLoader({ - gltf: gltf, - textureInfo: textureIds[textureId], - gltfResource: gltfResource, - baseResource: baseResource, - supportedImageFormats: supportedImageFormats, - frameState: frameState, - asynchronous: asynchronous, - }); - gpmLoader._textureLoaders.push(textureLoader); - gpmLoader._textureIds.push(textureId); - texturePromises.push(textureLoader.load()); - } - } - - return Promise.all(texturePromises); -} - -/** - * A static mapping from PPE texture property identifier keys - * to `MetadataSchema` instances. This is used to create each - * schema (with a certain structure) only ONCE in - * obtainPpeTexturesMetadataSchema - * - * @private - */ -GltfGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); - -/** - * Create the JSON description of a metadata class that treats - * the given PPE texture as a property texture property(!). - * - * @param {any} ppeTexture - The PPE texture - * @param {number} index - The index of the texture in the extension - * @returns The class JSON - */ -GltfGpmLoader.prototype.createPpeTextureClassJson = function ( - ppeTexture, - index -) { - const traits = ppeTexture.traits; - const ppePropertyName = traits.source; - - // XXX_UNCERTAINTY: The ppeTexture will have a structure - // like this: - // - //"ppeTextures" : [ - // { - // "traits" : { - // "source" : "SIGZ", - // "min" : 0.0, - // "max" : 16.0 - // }, - // "index" : 2, - // "noData" : 255, - // "offset" : 0.0, - // "scale" : 0.06274509803921569, - // "texCoord" : 1 - // }, - // - // Note that in GPM 1.2d, the min/max should be part of the - // texture, but in the actual data (GPM 1.2i), they are in - // the traits (ppeMetadata). And there, they/ seem to denote - // the min/max values that actually appear in the texture. - // So they are integrated into the 'scale' factor here: - const min = traits.min ?? 0; - const max = traits.max ?? 255; - const minMaxScale = 255.0 / (max - min); - const offset = ppeTexture.offset; - const scale = ppeTexture.scale * minMaxScale; - const classJson = { - name: `PPE texture class ${index}`, - properties: { - [ppePropertyName]: { - name: "PPE", - type: "SCALAR", - componentType: "UINT8", - normalized: true, - offset: offset, - scale: scale, - }, - }, - }; - return classJson; -}; - /** - * Returns the `MetadataSchema` for the PPE textures in this instance. - * - * This method will return a (statically/globally) cached metadata - * schema that reflects the structure of the PPE textures in this - * instance, creating and caching it if necessary. + * Creates a Matrix3 that describes a covariance matrix (which is + * symmetric) from the array containing the upper triangle, in + * column-major order. * - * For details on the cache key, see `collectPpeTexturePropertyIdentifiers` - * - * @returns The `MetadataSchema` + * @param {number[]} array The input array + * @returns The Matrix3 */ -GltfGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function () { - const ppeTexturePropertyIdentifiers = this.collectPpeTexturePropertyIdentifiers(); - const key = ppeTexturePropertyIdentifiers.toString(); - let ppeTexturesMetadataSchema = GltfGpmLoader.ppeTexturesMetadataSchemaCache.get( - key - ); - if (defined(ppeTexturesMetadataSchema)) { - // XXX_UNCERTAINTY Debug log - //console.log(`Using cached schema for GPM PPE textures with key ${key}`); - return ppeTexturesMetadataSchema; - } - - // XXX_UNCERTAINTY Debug log - if caching works, this should be printed only ONCE! - console.log(`Creating schema for GPM PPE textures with key ${key}`); - const schemaId = `PPE_TEXTURE_SCHEMA_${GltfGpmLoader.ppeTexturesMetadataSchemaCache.size}`; - const ppeTexturesMetadataSchemaJson = { - id: schemaId, - classes: {}, - }; - - const extension = this._extension; - if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - const classId = `ppeTexture_${i}`; - const classJson = this.createPpeTextureClassJson(ppeTexture, i); - ppeTexturesMetadataSchemaJson.classes[classId] = classJson; - } - } - - ppeTexturesMetadataSchema = MetadataSchema.fromJson( - ppeTexturesMetadataSchemaJson +function createCovarianceMatrixFromUpperTriangle(array) { + const covarianceMatrix = new Matrix3( + array[0], + array[1], + array[3], + array[1], + array[2], + array[4], + array[3], + array[4], + array[5] ); - GltfGpmLoader.ppeTexturesMetadataSchemaCache.set( - key, - ppeTexturesMetadataSchema - ); - return ppeTexturesMetadataSchema; -}; - -/** - * Creates an array of strings that serve as identifiers for PPE textures. - * - * Each glTF may define multiple `ppeTexture` objects within the - * `NGA_gpm_local` extensions. Each of these textures corresponds - * to one 'property texture property(!)' in a metadata schema. - * - * This method will create an array where each element is a (JSON) - * string representation of the parts of a GPM PPE texture definition - * that are relevant for distinguishing two PPE textures in terms - * of their structure within a `StructuralMetadata`. - * - * @returns The identifiers - */ -GltfGpmLoader.prototype.collectPpeTexturePropertyIdentifiers = function () { - const extension = this._extension; - const ppeTexturePropertyIdentifiers = []; - if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - // The following will create an identifier that can be used - // to define two PPE textures as "representing the same - // property texture property" within a structural metadata - // schema. - const classJson = this.createPpeTextureClassJson(ppeTexture, i); - const ppeTexturePropertyIdentifier = JSON.stringify(classJson); - ppeTexturePropertyIdentifiers.push(ppeTexturePropertyIdentifier); - } - } - return ppeTexturePropertyIdentifiers; -}; + return covarianceMatrix; +} /** * Processes the resource until it becomes ready. @@ -362,108 +150,122 @@ GltfGpmLoader.prototype.process = function (frameState) { if (this._state === ResourceLoaderState.READY) { return true; } - + if (this._state === ResourceLoaderState.FAILED) { + return true; + } if (this._state !== ResourceLoaderState.LOADED) { return false; } - const textureLoaders = this._textureLoaders; - const textureLoadersLength = textureLoaders.length; - let ready = true; - for (let i = 0; i < textureLoadersLength; ++i) { - const textureLoader = textureLoaders[i]; - const textureReady = textureLoader.process(frameState); - ready = ready && textureReady; - } + console.log("PARSE AND STORE HERE! ", this._extension); - if (!ready) { - return false; - } + const extensionJson = this._extension; - const textures = {}; - for (let i = 0; i < this._textureIds.length; ++i) { - const textureId = this._textureIds[i]; - const textureLoader = textureLoaders[i]; - if (!textureLoader.isDestroyed()) { - textures[textureId] = textureLoader.texture; + const storageType = extensionJson.storageType; + if (storageType === StorageType.Direct) { + const anchorPointsDirect = []; + const anchorPointsDirectJson = extensionJson.anchorPointsDirect; + for (const anchorPointDirectJson of anchorPointsDirectJson) { + const position = Cartesian3.fromArray( + anchorPointDirectJson.position, + 0, + new Cartesian3() + ); + const adjustmentParams = Cartesian3.fromArray( + anchorPointDirectJson.adjustmentParams, + 0, + new Cartesian3() + ); + const anchorPointDirect = new AnchorPointDirect({ + position: position, + adjustmentParams: adjustmentParams, + }); + anchorPointsDirect.push(anchorPointDirect); } + const covarianceDirect = createCovarianceMatrixFromUpperTriangle( + extensionJson.covarianceDirectUpperTriangle + ); + + this._gltfGpmLocal = new GltfGpmLocal({ + storageType: storageType, + anchorPointsDirect: anchorPointsDirect, + covarianceDirect: covarianceDirect, + }); + this._state = ResourceLoaderState.READY; + return true; } - - const ppeTexturesMetadataSchema = this.obtainPpeTexturesMetadataSchema(); - const extension = this._extension; - const propertyTextures = []; - if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - const classId = `ppeTexture_${i}`; - const traits = ppeTexture.traits; - const ppePropertyName = traits.source; - const metadataClass = ppeTexturesMetadataSchema.classes[classId]; - - // XXX_UNCERTAINTY Debug log - //console.log( - // `Creating property texture with class ${classId} and property ${ppePropertyName}` - //); - - const ppeTextureAsPropertyTexture = { - class: classId, - properties: { - [ppePropertyName]: { - index: ppeTexture.index, - texCoord: ppeTexture.texCoord, - }, - }, - }; - propertyTextures.push( - new PropertyTexture({ - id: i, - name: ppeTexture.name, - propertyTexture: ppeTextureAsPropertyTexture, - class: metadataClass, - textures: textures, - }) + if (storageType === StorageType.Indirect) { + const anchorPointsIndirect = []; + const anchorPointsIndirectJson = extensionJson.anchorPointsIndirect; + for (const anchorPointIndirectJson of anchorPointsIndirectJson) { + const position = Cartesian3.fromArray( + anchorPointIndirectJson.position, + 0, + new Cartesian3() + ); + const adjustmentParams = Cartesian3.fromArray( + anchorPointIndirectJson.adjustmentParams, + 0, + new Cartesian3() + ); + const covarianceMatrix = createCovarianceMatrixFromUpperTriangle( + anchorPointIndirectJson.covarianceMatrix ); + const anchorPointIndirect = new AnchorPointIndirect({ + position: position, + adjustmentParams: adjustmentParams, + covarianceMatrix: covarianceMatrix, + }); + anchorPointsIndirect.push(anchorPointIndirect); } - } - const structuralMetadata = new StructuralMetadata({ - schema: ppeTexturesMetadataSchema, - propertyTables: [], - propertyTextures: propertyTextures, - propertyAttributes: [], - statistics: extension.statistics, - extras: extension.extras, - extensions: extension.extensions, - }); - this._structuralMetadata = structuralMetadata; + const intraTileCorrelationGroupsJson = + extensionJson.intraTileCorrelationGroups; + const intraTileCorrelationGroups = []; - this._state = ResourceLoaderState.READY; - return true; -}; + for (const correlationGroupJson of intraTileCorrelationGroupsJson) { + const groupFlags = correlationGroupJson.groupFlags; + const rotationThetas = Cartesian3.fromArray( + correlationGroupJson.rotationThetas, + 0, + new Cartesian3() + ); + const params = []; + for (const paramJson of correlationGroupJson.params) { + const param = new Spdcf({ + A: paramJson.A, + alpha: paramJson.alpha, + beta: paramJson.beta, + T: paramJson.T, + }); + params.push(param); + } + const correlationGroup = new CorrelationGroup({ + groupFlags: groupFlags, + rotationThetas: rotationThetas, + params: params, + }); + intraTileCorrelationGroups.push(correlationGroup); + } -function unloadTextures(gpmLoader) { - const textureLoaders = gpmLoader._textureLoaders; - const textureLoadersLength = textureLoaders.length; - for (let i = 0; i < textureLoadersLength; ++i) { - ResourceCache.unload(textureLoaders[i]); + this._gltfGpmLocal = new GltfGpmLocal({ + storageType: storageType, + anchorPointsIndirect: anchorPointsIndirect, + intraTileCorrelationGroups: intraTileCorrelationGroups, + }); + this._state = ResourceLoaderState.READY; + return true; } - gpmLoader._textureLoaders.length = 0; - gpmLoader._textureIds.length = 0; -} + this._state = ResourceLoaderState.FAILED; + return false; +}; /** * Unloads the resource. * @private */ GltfGpmLoader.prototype.unload = function () { - unloadTextures(this); - - if (defined(this._schemaLoader)) { - ResourceCache.unload(this._schemaLoader); - } - this._schemaLoader = undefined; - - this._structuralMetadata = undefined; + this._gltfGpmLocal = undefined; }; export default GltfGpmLoader; diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 18f825a6ae4..490cbfb087a 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -29,6 +29,8 @@ import ResourceCache from "./ResourceCache.js"; import ResourceLoader from "./ResourceLoader.js"; import SupportedImageFormats from "./SupportedImageFormats.js"; import VertexAttributeSemantic from "./VertexAttributeSemantic.js"; +import GltfGpmLoader from "./GltfGpmLoader.js"; +import GltfMeshPrimitiveGpmLoader from "./GltfMeshPrimitiveGpmLoader.js"; const { Attribute, @@ -260,6 +262,8 @@ function GltfLoader(options) { this._geometryLoaders = []; this._geometryCallbacks = []; this._structuralMetadataLoader = undefined; + this._gpmLoader = undefined; + this._meshPrimitiveGpmLoader = undefined; this._loadResourcesPromise = undefined; this._resourcesLoaded = false; this._texturesLoaded = false; @@ -471,6 +475,29 @@ function processLoaders(loader, frameState) { ready = ready && metadataReady; } + const gpmLoader = loader._gpmLoader; + if (defined(gpmLoader)) { + const gpmReady = gpmLoader.process(frameState); + if (gpmReady) { + // XXX + console.log("Got GPM data, storing ", gpmLoader._gltfGpmLocal); + loader._components.extensions["NGA_gpm_local"] = gpmLoader._gltfGpmLocal; + } + ready = ready && gpmReady; + } + + const meshPrimitiveGpmLoader = loader._meshPrimitiveGpmLoader; + if (defined(meshPrimitiveGpmLoader)) { + const metadataReady = meshPrimitiveGpmLoader.process(frameState); + if (metadataReady) { + // XXX + console.log("Overwriting structural metadata with GPM data"); + loader._components.structuralMetadata = + meshPrimitiveGpmLoader.structuralMetadata; + } + ready = ready && metadataReady; + } + if (ready) { // Geometry requires further processing loader._state = GltfLoaderState.POST_PROCESSING; @@ -2453,6 +2480,32 @@ async function loadStructuralMetadata( return structuralMetadataLoader.load(); } +async function loadGpm(loader, gltf, extension) { + const gpmLoader = new GltfGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: loader._gltfResource, + baseResource: loader._baseResource, + asynchronous: loader._asynchronous, + }); + loader._gpmLoader = gpmLoader; + return gpmLoader.load(); +} + +async function loadMeshPrimitiveGpm(loader, gltf, extension, frameState) { + const meshPrimitiveGpmLoader = new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: loader._gltfResource, + baseResource: loader._baseResource, + supportedImageFormats: loader._supportedImageFormats, + frameState: frameState, + asynchronous: loader._asynchronous, + }); + loader._meshPrimitiveGpmLoader = meshPrimitiveGpmLoader; + return meshPrimitiveGpmLoader.load(); +} + function loadAnimationSampler(loader, gltfSampler) { const animationSampler = new AnimationSampler(); const accessors = loader.gltfJson.accessors; @@ -2681,6 +2734,39 @@ function parse(loader, frameState) { loader._loaderPromises.push(promise); } + // Load NGA_gpm_local from root object + const gpmExtension = extensions.NGA_gpm_local; + if (defined(gpmExtension)) { + console.log("Loading GPM from root"); + const promise = loadGpm(loader, gltf, gpmExtension); + loader._loaderPromises.push(promise); + } + + // Load NGA_gpm_local from mesh primitives + const meshes = gltf.meshes; + if (defined(meshes)) { + for (const mesh of meshes) { + const primitives = mesh.primitives; + if (defined(primitives)) { + for (const primitive of primitives) { + const primitiveExtensions = primitive.extensions; + const meshPrimitiveGpmExtension = primitiveExtensions.NGA_gpm_local; + if (defined(meshPrimitiveGpmExtension)) { + // XXX + console.log("Loading GPM from mesh primitive"); + const promise = loadMeshPrimitiveGpm( + loader, + gltf, + meshPrimitiveGpmExtension, + frameState + ); + loader._loaderPromises.push(promise); + } + } + } + } + } + // Gather promises and handle any errors const readyPromises = []; readyPromises.push.apply(readyPromises, loader._loaderPromises); @@ -2745,6 +2831,23 @@ function unloadStructuralMetadata(loader) { } } +function unloadGpm(loader) { + if (defined(loader._gpmLoader) && !loader._gpmLoader.isDestroyed()) { + loader._gpmLoader.destroy(); + loader._gpmLoader = undefined; + } +} + +function unloadMeshPrimitiveGpm(loader) { + if ( + defined(loader._meshPrimitiveGpmLoader) && + !loader._meshPrimitiveGpmLoader.isDestroyed() + ) { + loader._meshPrimitiveGpmLoader.destroy(); + loader._meshPrimitiveGpmLoader = undefined; + } +} + /** * Returns whether the resource has been unloaded. * @private @@ -2768,6 +2871,8 @@ GltfLoader.prototype.unload = function () { unloadGeometry(this); unloadGeneratedAttributes(this); unloadStructuralMetadata(this); + unloadGpm(this); + unloadMeshPrimitiveGpm(this); this._components = undefined; this._typedArray = undefined; diff --git a/packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js new file mode 100644 index 00000000000..724b1d8b1a4 --- /dev/null +++ b/packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js @@ -0,0 +1,465 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import defined from "../Core/defined.js"; +import ResourceCache from "./ResourceCache.js"; +import ResourceLoader from "./ResourceLoader.js"; +import ResourceLoaderState from "./ResourceLoaderState.js"; +import PropertyTexture from "./PropertyTexture.js"; +import StructuralMetadata from "./StructuralMetadata.js"; +import MetadataSchema from "./MetadataSchema.js"; + +/** + * Loads glTF NGA_gpm_local from a glTF mesh primitive. + *

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

+ * Implementation note: This is an experimental implementation. It is based + * on a GltfStructuralMetadataLoader, by removing everything that is not + * related to property textures, and translating the "ppeTextures" of + * the NGA_gpm_local extension into property texture properties. These will + * be returned as part of a `StructuralMetadata` object, which may override + * any `StructuralMetadata` that was read directly from the glTF. + * + * @alias GltfMeshPrimitiveGpmLoader + * @constructor + * @augments ResourceLoader + * + * @param {object} options Object with the following properties: + * @param {object} options.gltf The glTF JSON. + * @param {string} [options.extension] The NGA_gpm_local extension object. + * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. + * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. + * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats. + * @param {FrameState} options.frameState The frame state. + * @param {string} [options.cacheKey] The cache key of the resource. + * @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. + * + * @private + * @experimental This feature is subject to change without Cesium's standard deprecation policy. + */ +function GltfMeshPrimitiveGpmLoader(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + const gltf = options.gltf; + const extension = options.extension; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const supportedImageFormats = options.supportedImageFormats; + const frameState = options.frameState; + const cacheKey = options.cacheKey; + const asynchronous = defaultValue(options.asynchronous, true); + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.object("options.extension", extension); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); + Check.typeOf.object("options.frameState", frameState); + //>>includeEnd('debug'); + + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._extension = extension; + this._supportedImageFormats = supportedImageFormats; + this._frameState = frameState; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._textureLoaders = []; + this._textureIds = []; + this._structuralMetadata = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; +} + +if (defined(Object.create)) { + GltfMeshPrimitiveGpmLoader.prototype = Object.create( + ResourceLoader.prototype + ); + GltfMeshPrimitiveGpmLoader.prototype.constructor = GltfMeshPrimitiveGpmLoader; +} + +Object.defineProperties(GltfMeshPrimitiveGpmLoader.prototype, { + /** + * The cache key of the resource. + * + * @memberof GltfMeshPrimitiveGpmLoader.prototype + * + * @type {string} + * @readonly + * @private + */ + cacheKey: { + get: function () { + return this._cacheKey; + }, + }, + /** + * The parsed structural metadata + * + * @memberof GltfMeshPrimitiveGpmLoader.prototype + * + * @type {StructuralMetadata} + * @readonly + * @private + */ + structuralMetadata: { + get: function () { + return this._structuralMetadata; + }, + }, +}); + +async function loadResources(loader) { + try { + const texturesPromise = loadTextures(loader); + await texturesPromise; + + if (loader.isDestroyed()) { + return; + } + + loader._gltf = undefined; // No longer need to hold onto the glTF + + loader._state = ResourceLoaderState.LOADED; + return loader; + } catch (error) { + if (loader.isDestroyed()) { + return; + } + + loader.unload(); + loader._state = ResourceLoaderState.FAILED; + const errorMessage = "Failed to load GPM data"; + throw loader.getError(errorMessage, error); + } +} + +/** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ +GltfMeshPrimitiveGpmLoader.prototype.load = function () { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; +}; + +function gatherUsedTextureIds(gpmExtension) { + // Gather the used textures + const textureIds = {}; + const ppeTextures = gpmExtension.ppeTextures; + if (defined(ppeTextures)) { + for (let i = 0; i < ppeTextures.length; i++) { + const ppeTexture = ppeTextures[i]; + // The texture is a valid textureInfo. + textureIds[ppeTexture.index] = ppeTexture; + } + } + return textureIds; +} + +function loadTextures(meshPrimitiveGpmLoader) { + let textureIds; + if (defined(meshPrimitiveGpmLoader._extension)) { + textureIds = gatherUsedTextureIds(meshPrimitiveGpmLoader._extension); + } + + const gltf = meshPrimitiveGpmLoader._gltf; + const gltfResource = meshPrimitiveGpmLoader._gltfResource; + const baseResource = meshPrimitiveGpmLoader._baseResource; + const supportedImageFormats = meshPrimitiveGpmLoader._supportedImageFormats; + const frameState = meshPrimitiveGpmLoader._frameState; + const asynchronous = meshPrimitiveGpmLoader._asynchronous; + + // Load the textures + const texturePromises = []; + for (const textureId in textureIds) { + if (textureIds.hasOwnProperty(textureId)) { + const textureLoader = ResourceCache.getTextureLoader({ + gltf: gltf, + textureInfo: textureIds[textureId], + gltfResource: gltfResource, + baseResource: baseResource, + supportedImageFormats: supportedImageFormats, + frameState: frameState, + asynchronous: asynchronous, + }); + meshPrimitiveGpmLoader._textureLoaders.push(textureLoader); + meshPrimitiveGpmLoader._textureIds.push(textureId); + texturePromises.push(textureLoader.load()); + } + } + + return Promise.all(texturePromises); +} + +/** + * A static mapping from PPE texture property identifier keys + * to `MetadataSchema` instances. This is used to create each + * schema (with a certain structure) only ONCE in + * obtainPpeTexturesMetadataSchema + * + * @private + */ +GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); + +/** + * Create the JSON description of a metadata class that treats + * the given PPE texture as a property texture property(!). + * + * @param {any} ppeTexture - The PPE texture + * @param {number} index - The index of the texture in the extension + * @returns The class JSON + */ +GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( + ppeTexture, + index +) { + const traits = ppeTexture.traits; + const ppePropertyName = traits.source; + + // XXX_UNCERTAINTY: The ppeTexture will have a structure + // like this: + // + //"ppeTextures" : [ + // { + // "traits" : { + // "source" : "SIGZ", + // "min" : 0.0, + // "max" : 16.0 + // }, + // "index" : 2, + // "noData" : 255, + // "offset" : 0.0, + // "scale" : 0.06274509803921569, + // "texCoord" : 1 + // }, + // + // Note that in GPM 1.2d, the min/max should be part of the + // texture, but in the actual data (GPM 1.2i), they are in + // the traits (ppeMetadata). And there, they/ seem to denote + // the min/max values that actually appear in the texture. + // So they are integrated into the 'scale' factor here: + const min = traits.min ?? 0; + const max = traits.max ?? 255; + const minMaxScale = 255.0 / (max - min); + const offset = ppeTexture.offset; + const scale = ppeTexture.scale * minMaxScale; + const classJson = { + name: `PPE texture class ${index}`, + properties: { + [ppePropertyName]: { + name: "PPE", + type: "SCALAR", + componentType: "UINT8", + normalized: true, + offset: offset, + scale: scale, + }, + }, + }; + return classJson; +}; + +/** + * Returns the `MetadataSchema` for the PPE textures in this instance. + * + * This method will return a (statically/globally) cached metadata + * schema that reflects the structure of the PPE textures in this + * instance, creating and caching it if necessary. + * + * For details on the cache key, see `collectPpeTexturePropertyIdentifiers` + * + * @returns The `MetadataSchema` + */ +GltfMeshPrimitiveGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function () { + const ppeTexturePropertyIdentifiers = this.collectPpeTexturePropertyIdentifiers(); + const key = ppeTexturePropertyIdentifiers.toString(); + let ppeTexturesMetadataSchema = GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache.get( + key + ); + if (defined(ppeTexturesMetadataSchema)) { + // XXX_UNCERTAINTY Debug log + //console.log(`Using cached schema for GPM PPE textures with key ${key}`); + return ppeTexturesMetadataSchema; + } + + // XXX_UNCERTAINTY Debug log - if caching works, this should be printed only ONCE! + console.log(`Creating schema for GPM PPE textures with key ${key}`); + const schemaId = `PPE_TEXTURE_SCHEMA_${GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache.size}`; + const ppeTexturesMetadataSchemaJson = { + id: schemaId, + classes: {}, + }; + + const extension = this._extension; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const classJson = this.createPpeTextureClassJson(ppeTexture, i); + ppeTexturesMetadataSchemaJson.classes[classId] = classJson; + } + } + + ppeTexturesMetadataSchema = MetadataSchema.fromJson( + ppeTexturesMetadataSchemaJson + ); + GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache.set( + key, + ppeTexturesMetadataSchema + ); + return ppeTexturesMetadataSchema; +}; + +/** + * Creates an array of strings that serve as identifiers for PPE textures. + * + * Each glTF may define multiple `ppeTexture` objects within the + * `NGA_gpm_local` extensions. Each of these textures corresponds + * to one 'property texture property(!)' in a metadata schema. + * + * This method will create an array where each element is a (JSON) + * string representation of the parts of a GPM PPE texture definition + * that are relevant for distinguishing two PPE textures in terms + * of their structure within a `StructuralMetadata`. + * + * @returns The identifiers + */ +GltfMeshPrimitiveGpmLoader.prototype.collectPpeTexturePropertyIdentifiers = function () { + const extension = this._extension; + const ppeTexturePropertyIdentifiers = []; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + // The following will create an identifier that can be used + // to define two PPE textures as "representing the same + // property texture property" within a structural metadata + // schema. + const classJson = this.createPpeTextureClassJson(ppeTexture, i); + const ppeTexturePropertyIdentifier = JSON.stringify(classJson); + ppeTexturePropertyIdentifiers.push(ppeTexturePropertyIdentifier); + } + } + return ppeTexturePropertyIdentifiers; +}; + +/** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ +GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state !== ResourceLoaderState.LOADED) { + return false; + } + + const textureLoaders = this._textureLoaders; + const textureLoadersLength = textureLoaders.length; + let ready = true; + for (let i = 0; i < textureLoadersLength; ++i) { + const textureLoader = textureLoaders[i]; + const textureReady = textureLoader.process(frameState); + ready = ready && textureReady; + } + + if (!ready) { + return false; + } + + const textures = {}; + for (let i = 0; i < this._textureIds.length; ++i) { + const textureId = this._textureIds[i]; + const textureLoader = textureLoaders[i]; + if (!textureLoader.isDestroyed()) { + textures[textureId] = textureLoader.texture; + } + } + + const ppeTexturesMetadataSchema = this.obtainPpeTexturesMetadataSchema(); + const extension = this._extension; + const propertyTextures = []; + if (defined(extension.ppeTextures)) { + for (let i = 0; i < extension.ppeTextures.length; i++) { + const ppeTexture = extension.ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const traits = ppeTexture.traits; + const ppePropertyName = traits.source; + const metadataClass = ppeTexturesMetadataSchema.classes[classId]; + + // XXX_UNCERTAINTY Debug log + //console.log( + // `Creating property texture with class ${classId} and property ${ppePropertyName}` + //); + + const ppeTextureAsPropertyTexture = { + class: classId, + properties: { + [ppePropertyName]: { + index: ppeTexture.index, + texCoord: ppeTexture.texCoord, + }, + }, + }; + propertyTextures.push( + new PropertyTexture({ + id: i, + name: ppeTexture.name, + propertyTexture: ppeTextureAsPropertyTexture, + class: metadataClass, + textures: textures, + }) + ); + } + } + + const structuralMetadata = new StructuralMetadata({ + schema: ppeTexturesMetadataSchema, + propertyTables: [], + propertyTextures: propertyTextures, + propertyAttributes: [], + statistics: extension.statistics, + extras: extension.extras, + extensions: extension.extensions, + }); + this._structuralMetadata = structuralMetadata; + + this._state = ResourceLoaderState.READY; + return true; +}; + +function unloadTextures(meshPrimitiveGpmLoader) { + const textureLoaders = meshPrimitiveGpmLoader._textureLoaders; + const textureLoadersLength = textureLoaders.length; + for (let i = 0; i < textureLoadersLength; ++i) { + ResourceCache.unload(textureLoaders[i]); + } + meshPrimitiveGpmLoader._textureLoaders.length = 0; + meshPrimitiveGpmLoader._textureIds.length = 0; +} + +/** + * Unloads the resource. + * @private + */ +GltfMeshPrimitiveGpmLoader.prototype.unload = function () { + unloadTextures(this); + this._structuralMetadata = undefined; +}; + +export default GltfMeshPrimitiveGpmLoader; diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index e5415a11587..7d481d2088d 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -1772,6 +1772,24 @@ Model.prototype.applyArticulations = function () { this._sceneGraph.applyArticulations(); }; +/** + * XXX_TODO_COMMENT + * + * @exception {DeveloperError} The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true. + */ +Model.prototype.getExtension = function (extensionName) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("extensionName", extensionName); + if (!this._ready) { + throw new DeveloperError( + "The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true." + ); + } + //>>includeEnd('debug'); + const components = this._loader.components; + return components.extensions[extensionName]; +}; + /** * Marks the model's {@link Model#style} as dirty, which forces all features * to re-evaluate the style in the next frame the model is visible. diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js index a32206d3dba..f2240619e74 100644 --- a/packages/engine/Source/Scene/Model/Model3DTileContent.js +++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js @@ -152,6 +152,12 @@ Object.defineProperties(Model3DTileContent.prototype, { }, }); +Model3DTileContent.prototype.getExtension = function (extensionName) { + const model = this._model; + const extension = model.getExtension(extensionName); + return extension; +}; + Model3DTileContent.prototype.getFeature = function (featureId) { const model = this._model; const featureTableId = model.featureTableId; @@ -468,9 +474,11 @@ Model3DTileContent.prototype.pick = function (ray, frameState, result) { }; function makeModelOptions(tileset, tile, content, additionalOptions) { + console.log("XXX KEEPING glTF in Model3DTileContent"); const mainOptions = { cull: false, // The model is already culled by 3D Tiles - releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory + //releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory + releaseGltfJson: false, opaquePass: Pass.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass modelMatrix: tile.computedTransform, upAxis: tileset._modelUpAxis, diff --git a/packages/engine/Source/Scene/ModelComponents.js b/packages/engine/Source/Scene/ModelComponents.js index 42b3834c6db..428e410dd8a 100644 --- a/packages/engine/Source/Scene/ModelComponents.js +++ b/packages/engine/Source/Scene/ModelComponents.js @@ -1141,6 +1141,12 @@ function Components() { * @private */ this.transform = Matrix4.clone(Matrix4.IDENTITY); + + /** + * A mapping from extension names like `"EXT_example_extension"` to + * the object that was created from the extension input + */ + this.extensions = {}; } /** From 7e1479105782cf4b385acdbe4b877abe1aa1d14b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 19 Sep 2024 13:49:18 +0200 Subject: [PATCH 04/38] Move loaders to extension directory --- packages/engine/Source/Scene/GltfLoader.js | 4 +-- .../Extensions/Gpm}/GltfGpmLoader.js | 26 +++++++++---------- .../Gpm}/GltfMeshPrimitiveGpmLoader.js | 18 ++++++------- 3 files changed, 24 insertions(+), 24 deletions(-) rename packages/engine/Source/Scene/{ => Model/Extensions/Gpm}/GltfGpmLoader.js (90%) rename packages/engine/Source/Scene/{ => Model/Extensions/Gpm}/GltfMeshPrimitiveGpmLoader.js (96%) diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 490cbfb087a..bb25a498d4d 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -29,8 +29,8 @@ import ResourceCache from "./ResourceCache.js"; import ResourceLoader from "./ResourceLoader.js"; import SupportedImageFormats from "./SupportedImageFormats.js"; import VertexAttributeSemantic from "./VertexAttributeSemantic.js"; -import GltfGpmLoader from "./GltfGpmLoader.js"; -import GltfMeshPrimitiveGpmLoader from "./GltfMeshPrimitiveGpmLoader.js"; +import GltfGpmLoader from "./Model/Extensions/Gpm/GltfGpmLoader.js"; +import GltfMeshPrimitiveGpmLoader from "./Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js"; const { Attribute, diff --git a/packages/engine/Source/Scene/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js similarity index 90% rename from packages/engine/Source/Scene/GltfGpmLoader.js rename to packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index 4fe1cf50c46..520a96b4476 100644 --- a/packages/engine/Source/Scene/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -1,16 +1,16 @@ -import Cartesian3 from "../Core/Cartesian3.js"; -import Check from "../Core/Check.js"; -import defaultValue from "../Core/defaultValue.js"; -import defined from "../Core/defined.js"; -import Matrix3 from "../Core/Matrix3.js"; -import AnchorPointDirect from "./Model/Extensions/Gpm/AnchorPointDirect.js"; -import AnchorPointIndirect from "./Model/Extensions/Gpm/AnchorPointIndirect.js"; -import CorrelationGroup from "./Model/Extensions/Gpm/CorrelationGroup.js"; -import GltfGpmLocal from "./Model/Extensions/Gpm/GltfGpmLocal.js"; -import Spdcf from "./Model/Extensions/Gpm/Spdcf.js"; -import StorageType from "./Model/Extensions/Gpm/StorageType.js"; -import ResourceLoader from "./ResourceLoader.js"; -import ResourceLoaderState from "./ResourceLoaderState.js"; +import Cartesian3 from "../../../../Core/Cartesian3.js"; +import Check from "../../../../Core/Check.js"; +import defaultValue from "../../../../Core/defaultValue.js"; +import defined from "../../../../Core/defined.js"; +import Matrix3 from "../../../../Core/Matrix3.js"; +import ResourceLoader from "./../../../ResourceLoader.js"; +import ResourceLoaderState from "./../../../ResourceLoaderState.js"; +import AnchorPointDirect from "./AnchorPointDirect.js"; +import AnchorPointIndirect from "./AnchorPointIndirect.js"; +import CorrelationGroup from "./CorrelationGroup.js"; +import GltfGpmLocal from "./GltfGpmLocal.js"; +import Spdcf from "./Spdcf.js"; +import StorageType from "./StorageType.js"; /** * Loads glTF NGA_gpm_local from the root of a glTF object diff --git a/packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js similarity index 96% rename from packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js rename to packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index 724b1d8b1a4..0668183ea0f 100644 --- a/packages/engine/Source/Scene/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -1,12 +1,12 @@ -import Check from "../Core/Check.js"; -import defaultValue from "../Core/defaultValue.js"; -import defined from "../Core/defined.js"; -import ResourceCache from "./ResourceCache.js"; -import ResourceLoader from "./ResourceLoader.js"; -import ResourceLoaderState from "./ResourceLoaderState.js"; -import PropertyTexture from "./PropertyTexture.js"; -import StructuralMetadata from "./StructuralMetadata.js"; -import MetadataSchema from "./MetadataSchema.js"; +import Check from "../../../../Core/Check.js"; +import defaultValue from "../../../../Core/defaultValue.js"; +import defined from "../../../../Core/defined.js"; +import ResourceCache from "../../../ResourceCache.js"; +import ResourceLoader from "../../../ResourceLoader.js"; +import ResourceLoaderState from "../../../ResourceLoaderState.js"; +import PropertyTexture from "../../../PropertyTexture.js"; +import StructuralMetadata from "../../../StructuralMetadata.js"; +import MetadataSchema from "../../../MetadataSchema.js"; /** * Loads glTF NGA_gpm_local from a glTF mesh primitive. From 6ee73444dbe75acd6a9b7e1450af023c4a297ec8 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 19 Sep 2024 15:16:48 +0200 Subject: [PATCH 05/38] Added implementation note in GltfGpmLoader --- .../Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index 520a96b4476..3091184142a 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -19,6 +19,13 @@ import StorageType from "./StorageType.js"; *

* Implementation note: This is an experimental implementation. * + * TODO This implements ResourceLoader, even though it does not really + * load other resources (in contrast to GltfMeshPrimitiveGpmLoader, + * which loads textures). Parsing the JSON into a GltfGpmLocal + * could be done directly (synchronously in the GltfLoader class). + * But it should be carved out into a dedicated class, regardless + * of how this is eventually USED from the GltfLoader. + * * @alias GltfGpmLoader * @constructor * @augments ResourceLoader From c2a064c9bb2ebd7ab610f683a7dadc511fd7ae61 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 19 Sep 2024 15:17:18 +0200 Subject: [PATCH 06/38] Fix handling of min, max, offset, scale for PPE textures --- .../Gpm/GltfMeshPrimitiveGpmLoader.js | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index 0668183ea0f..c97ae55d7c6 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -224,8 +224,7 @@ GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( const traits = ppeTexture.traits; const ppePropertyName = traits.source; - // XXX_UNCERTAINTY: The ppeTexture will have a structure - // like this: + // The ppeTexture will have a structure like this: // //"ppeTextures" : [ // { @@ -241,16 +240,16 @@ GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( // "texCoord" : 1 // }, // - // Note that in GPM 1.2d, the min/max should be part of the - // texture, but in the actual data (GPM 1.2i), they are in - // the traits (ppeMetadata). And there, they/ seem to denote - // the min/max values that actually appear in the texture. - // So they are integrated into the 'scale' factor here: - const min = traits.min ?? 0; - const max = traits.max ?? 255; - const minMaxScale = 255.0 / (max - min); - const offset = ppeTexture.offset; - const scale = ppeTexture.scale * minMaxScale; + // This is translated into a single class property here, that defines + // the structure of the property texture property. + // + // Given that `offset` and `scale` may only be applied to integer + // property values when they are `normalized`, the values will be + // declared as `normalized` here. + // The and the normalization factor will later have to be + // cancelled out, when integrating the `scale` into the + // actual property texture property. In the property texture + // property, the `scale` has to be multiplied by 255. const classJson = { name: `PPE texture class ${index}`, properties: { @@ -259,8 +258,8 @@ GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( type: "SCALAR", componentType: "UINT8", normalized: true, - offset: offset, - scale: scale, + min: traits.min, + max: traits.max, }, }, }; @@ -407,12 +406,19 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { // `Creating property texture with class ${classId} and property ${ppePropertyName}` //); + // The class property has been declared as `normalized`, so + // that `offset` and `scale` can be applied. The normalization + // factor has to be cancelled out here, by multiplying the + // `scale` with 255. + const scale = ppeTexture.scale * 255.0; const ppeTextureAsPropertyTexture = { class: classId, properties: { [ppePropertyName]: { index: ppeTexture.index, texCoord: ppeTexture.texCoord, + offset: ppeTexture.offset, + scale: scale, }, }, }; From 50b072702973c8ca5b2dc39476509331b44db5e1 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 21 Sep 2024 17:27:19 +0200 Subject: [PATCH 07/38] Enable releaseGltfJson --- packages/engine/Source/Scene/Model/Model3DTileContent.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js index f2240619e74..1a1b8a1acea 100644 --- a/packages/engine/Source/Scene/Model/Model3DTileContent.js +++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js @@ -474,11 +474,9 @@ Model3DTileContent.prototype.pick = function (ray, frameState, result) { }; function makeModelOptions(tileset, tile, content, additionalOptions) { - console.log("XXX KEEPING glTF in Model3DTileContent"); const mainOptions = { cull: false, // The model is already culled by 3D Tiles - //releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory - releaseGltfJson: false, + 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._modelUpAxis, From cac091f95a14e28d762c38b2848873309f5b7ef7 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 21 Sep 2024 17:27:54 +0200 Subject: [PATCH 08/38] Let GltfGpmLoader not extend ResourceLoader --- packages/engine/Source/Scene/GltfLoader.js | 36 +- .../Model/Extensions/Gpm/GltfGpmLoader.js | 372 ++++++++---------- 2 files changed, 167 insertions(+), 241 deletions(-) diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index bb25a498d4d..d7abc6d3fee 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -262,7 +262,6 @@ function GltfLoader(options) { this._geometryLoaders = []; this._geometryCallbacks = []; this._structuralMetadataLoader = undefined; - this._gpmLoader = undefined; this._meshPrimitiveGpmLoader = undefined; this._loadResourcesPromise = undefined; this._resourcesLoaded = false; @@ -475,17 +474,6 @@ function processLoaders(loader, frameState) { ready = ready && metadataReady; } - const gpmLoader = loader._gpmLoader; - if (defined(gpmLoader)) { - const gpmReady = gpmLoader.process(frameState); - if (gpmReady) { - // XXX - console.log("Got GPM data, storing ", gpmLoader._gltfGpmLocal); - loader._components.extensions["NGA_gpm_local"] = gpmLoader._gltfGpmLocal; - } - ready = ready && gpmReady; - } - const meshPrimitiveGpmLoader = loader._meshPrimitiveGpmLoader; if (defined(meshPrimitiveGpmLoader)) { const metadataReady = meshPrimitiveGpmLoader.process(frameState); @@ -2480,18 +2468,6 @@ async function loadStructuralMetadata( return structuralMetadataLoader.load(); } -async function loadGpm(loader, gltf, extension) { - const gpmLoader = new GltfGpmLoader({ - gltf: gltf, - extension: extension, - gltfResource: loader._gltfResource, - baseResource: loader._baseResource, - asynchronous: loader._asynchronous, - }); - loader._gpmLoader = gpmLoader; - return gpmLoader.load(); -} - async function loadMeshPrimitiveGpm(loader, gltf, extension, frameState) { const meshPrimitiveGpmLoader = new GltfMeshPrimitiveGpmLoader({ gltf: gltf, @@ -2738,8 +2714,8 @@ function parse(loader, frameState) { const gpmExtension = extensions.NGA_gpm_local; if (defined(gpmExtension)) { console.log("Loading GPM from root"); - const promise = loadGpm(loader, gltf, gpmExtension); - loader._loaderPromises.push(promise); + const gltfGpmLocal = GltfGpmLoader.load(gpmExtension); + loader._components.extensions["NGA_gpm_local"] = gltfGpmLocal; } // Load NGA_gpm_local from mesh primitives @@ -2831,13 +2807,6 @@ function unloadStructuralMetadata(loader) { } } -function unloadGpm(loader) { - if (defined(loader._gpmLoader) && !loader._gpmLoader.isDestroyed()) { - loader._gpmLoader.destroy(); - loader._gpmLoader = undefined; - } -} - function unloadMeshPrimitiveGpm(loader) { if ( defined(loader._meshPrimitiveGpmLoader) && @@ -2871,7 +2840,6 @@ GltfLoader.prototype.unload = function () { unloadGeometry(this); unloadGeneratedAttributes(this); unloadStructuralMetadata(this); - unloadGpm(this); unloadMeshPrimitiveGpm(this); this._components = undefined; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index 3091184142a..c3a1a0c0bbb 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -1,10 +1,7 @@ import Cartesian3 from "../../../../Core/Cartesian3.js"; import Check from "../../../../Core/Check.js"; -import defaultValue from "../../../../Core/defaultValue.js"; -import defined from "../../../../Core/defined.js"; +import DeveloperError from "../../../../Core/DeveloperError.js"; import Matrix3 from "../../../../Core/Matrix3.js"; -import ResourceLoader from "./../../../ResourceLoader.js"; -import ResourceLoaderState from "./../../../ResourceLoaderState.js"; import AnchorPointDirect from "./AnchorPointDirect.js"; import AnchorPointIndirect from "./AnchorPointIndirect.js"; import CorrelationGroup from "./CorrelationGroup.js"; @@ -15,110 +12,19 @@ import StorageType from "./StorageType.js"; /** * Loads glTF NGA_gpm_local from the root of a glTF object *

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

* Implementation note: This is an experimental implementation. * - * TODO This implements ResourceLoader, even though it does not really - * load other resources (in contrast to GltfMeshPrimitiveGpmLoader, - * which loads textures). Parsing the JSON into a GltfGpmLocal - * could be done directly (synchronously in the GltfLoader class). - * But it should be carved out into a dedicated class, regardless - * of how this is eventually USED from the GltfLoader. - * * @alias GltfGpmLoader * @constructor - * @augments ResourceLoader * * @param {object} options Object with the following properties: * @param {object} options.gltf The glTF JSON. * @param {string} [options.extension] The NGA_gpm_local extension object. - * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. - * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. - * @param {string} [options.cacheKey] The cache key of the resource. - * @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. * * @private * @experimental This feature is subject to change without Cesium's standard deprecation policy. */ -function GltfGpmLoader(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - const gltf = options.gltf; - const extension = options.extension; - const gltfResource = options.gltfResource; - const baseResource = options.baseResource; - const cacheKey = options.cacheKey; - const asynchronous = defaultValue(options.asynchronous, true); - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.gltf", gltf); - Check.typeOf.object("options.extension", extension); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - //>>includeEnd('debug'); - - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._extension = extension; - this._cacheKey = cacheKey; - this._asynchronous = asynchronous; - this._gltfGpmLocal = undefined; - - // Immediately go into the "LOADED" state, since there - // are no resources to wait for - this._state = ResourceLoaderState.LOADED; - this._promise = undefined; -} - -if (defined(Object.create)) { - GltfGpmLoader.prototype = Object.create(ResourceLoader.prototype); - GltfGpmLoader.prototype.constructor = GltfGpmLoader; -} - -Object.defineProperties(GltfGpmLoader.prototype, { - /** - * The cache key of the resource. - * - * @memberof GltfGpmLoader.prototype - * - * @type {string} - * @readonly - * @private - */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, - /** - * The parsed GltfGpmLocal object - * - * @memberof GltfGpmLoader.prototype - * - * @type {GltfGpmLocal} - * @readonly - * @private - */ - gltfGpmLocal: { - get: function () { - return this._gltfGpmLocal; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfGpmLoader.prototype.load = function () { - if (defined(this._promise)) { - return this._promise; - } - this._promise = Promise.resolve(this); - return this._promise; -}; +function GltfGpmLoader() {} /** * Creates a Matrix3 that describes a covariance matrix (which is @@ -144,135 +50,187 @@ function createCovarianceMatrixFromUpperTriangle(array) { } /** - * Processes the resource until it becomes ready. + * Creates an `AnchorPointDirect` from the given JSON representation * - * @param {FrameState} frameState The frame state. + * @param {object} anchorPointDirectJson The input JSON + * @returns The `AnchorPointDirect` + */ +function createAnchorPointDirect(anchorPointDirectJson) { + const position = Cartesian3.fromArray( + anchorPointDirectJson.position, + 0, + new Cartesian3() + ); + const adjustmentParams = Cartesian3.fromArray( + anchorPointDirectJson.adjustmentParams, + 0, + new Cartesian3() + ); + const anchorPointDirect = new AnchorPointDirect({ + position: position, + adjustmentParams: adjustmentParams, + }); + return anchorPointDirect; +} + +/** + * Creates an `AnchorPointIndirect` from the given JSON representation + * + * @param {object} anchorPointIndirectJson The input JSON + * @returns The `AnchorPointIndirect` + */ +function createAnchorPointIndirect(anchorPointIndirectJson) { + const position = Cartesian3.fromArray( + anchorPointIndirectJson.position, + 0, + new Cartesian3() + ); + const adjustmentParams = Cartesian3.fromArray( + anchorPointIndirectJson.adjustmentParams, + 0, + new Cartesian3() + ); + const covarianceMatrix = createCovarianceMatrixFromUpperTriangle( + anchorPointIndirectJson.covarianceMatrix + ); + const anchorPointIndirect = new AnchorPointIndirect({ + position: position, + adjustmentParams: adjustmentParams, + covarianceMatrix: covarianceMatrix, + }); + return anchorPointIndirect; +} + +/** + * Creates a `CorrelationGroup` from the given JSON representation + * + * @param {object} correlationGroupJson The input JSON + * @returns The `CorrelationGroup` + */ +function createCorrelationGroup(correlationGroupJson) { + const groupFlags = correlationGroupJson.groupFlags; + const rotationThetas = Cartesian3.fromArray( + correlationGroupJson.rotationThetas, + 0, + new Cartesian3() + ); + const params = []; + for (const paramJson of correlationGroupJson.params) { + const param = new Spdcf({ + A: paramJson.A, + alpha: paramJson.alpha, + beta: paramJson.beta, + T: paramJson.T, + }); + params.push(param); + } + const correlationGroup = new CorrelationGroup({ + groupFlags: groupFlags, + rotationThetas: rotationThetas, + params: params, + }); + return correlationGroup; +} + +/** + * Loads the GPM data from the given JSON that was found as the + * `NGA_gpm_local` extension object in the root of the glTF. + * + * @param {object} gltfGpmLocalJson The extension object + * @returns {GltfGpmLocal} The parsed object * @private + * @throws DeveloperError When the given object is `undefined` + * or contains invalid structures */ -GltfGpmLoader.prototype.process = function (frameState) { +GltfGpmLoader.load = function (gltfGpmLocalJson) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); + Check.typeOf.object("gltfGpmLocalJson", gltfGpmLocalJson); //>>includeEnd('debug'); - if (this._state === ResourceLoaderState.READY) { - return true; + const storageType = gltfGpmLocalJson.storageType; + if (storageType === StorageType.Direct) { + return GltfGpmLoader.loadDirect(gltfGpmLocalJson); } - if (this._state === ResourceLoaderState.FAILED) { - return true; + if (storageType === StorageType.Indirect) { + return GltfGpmLoader.loadIndirect(gltfGpmLocalJson); } - if (this._state !== ResourceLoaderState.LOADED) { - return false; + throw new DeveloperError( + `Invalid storage type in NGA_gpm_local - expected 'Direct' or 'Indirect', but found ${storageType}` + ); +}; + +/** + * Loads the GPM data from the given JSON that was found as the + * `NGA_gpm_local` extension object in the root of the glTF, + * assuming that the `storageType` of the given object is + * `StorageType.Direct`. + * + * @param {object} gltfGpmLocalJson The extension object + * @returns {GltfGpmLocal} The parsed object + * @private + */ +GltfGpmLoader.loadDirect = function (gltfGpmLocalJson) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("gltfGpmLocalJson", gltfGpmLocalJson); + //>>includeEnd('debug'); + + const anchorPointsDirect = []; + const anchorPointsDirectJson = gltfGpmLocalJson.anchorPointsDirect; + for (const anchorPointDirectJson of anchorPointsDirectJson) { + const anchorPointDirect = createAnchorPointDirect(anchorPointDirectJson); + anchorPointsDirect.push(anchorPointDirect); } + const covarianceDirect = createCovarianceMatrixFromUpperTriangle( + gltfGpmLocalJson.covarianceDirectUpperTriangle + ); - console.log("PARSE AND STORE HERE! ", this._extension); + const gltfGpmLocal = new GltfGpmLocal({ + storageType: StorageType.Direct, + anchorPointsDirect: anchorPointsDirect, + covarianceDirect: covarianceDirect, + }); + return gltfGpmLocal; +}; - const extensionJson = this._extension; +/** + * Loads the GPM data from the given JSON that was found as the + * `NGA_gpm_local` extension object in the root of the glTF, + * assuming that the `storageType` of the given object is + * `StorageType.Indirect`. + * + * @param {object} gltfGpmLocalJson The extension object + * @returns {GltfGpmLocal} The parsed object + * @private + */ +GltfGpmLoader.loadIndirect = function (gltfGpmLocalJson) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("gltfGpmLocalJson", gltfGpmLocalJson); + //>>includeEnd('debug'); - const storageType = extensionJson.storageType; - if (storageType === StorageType.Direct) { - const anchorPointsDirect = []; - const anchorPointsDirectJson = extensionJson.anchorPointsDirect; - for (const anchorPointDirectJson of anchorPointsDirectJson) { - const position = Cartesian3.fromArray( - anchorPointDirectJson.position, - 0, - new Cartesian3() - ); - const adjustmentParams = Cartesian3.fromArray( - anchorPointDirectJson.adjustmentParams, - 0, - new Cartesian3() - ); - const anchorPointDirect = new AnchorPointDirect({ - position: position, - adjustmentParams: adjustmentParams, - }); - anchorPointsDirect.push(anchorPointDirect); - } - const covarianceDirect = createCovarianceMatrixFromUpperTriangle( - extensionJson.covarianceDirectUpperTriangle + const anchorPointsIndirect = []; + const anchorPointsIndirectJson = gltfGpmLocalJson.anchorPointsIndirect; + for (const anchorPointIndirectJson of anchorPointsIndirectJson) { + const anchorPointIndirect = createAnchorPointIndirect( + anchorPointIndirectJson ); - - this._gltfGpmLocal = new GltfGpmLocal({ - storageType: storageType, - anchorPointsDirect: anchorPointsDirect, - covarianceDirect: covarianceDirect, - }); - this._state = ResourceLoaderState.READY; - return true; + anchorPointsIndirect.push(anchorPointIndirect); } - if (storageType === StorageType.Indirect) { - const anchorPointsIndirect = []; - const anchorPointsIndirectJson = extensionJson.anchorPointsIndirect; - for (const anchorPointIndirectJson of anchorPointsIndirectJson) { - const position = Cartesian3.fromArray( - anchorPointIndirectJson.position, - 0, - new Cartesian3() - ); - const adjustmentParams = Cartesian3.fromArray( - anchorPointIndirectJson.adjustmentParams, - 0, - new Cartesian3() - ); - const covarianceMatrix = createCovarianceMatrixFromUpperTriangle( - anchorPointIndirectJson.covarianceMatrix - ); - const anchorPointIndirect = new AnchorPointIndirect({ - position: position, - adjustmentParams: adjustmentParams, - covarianceMatrix: covarianceMatrix, - }); - anchorPointsIndirect.push(anchorPointIndirect); - } - const intraTileCorrelationGroupsJson = - extensionJson.intraTileCorrelationGroups; - const intraTileCorrelationGroups = []; + const intraTileCorrelationGroupsJson = + gltfGpmLocalJson.intraTileCorrelationGroups; + const intraTileCorrelationGroups = []; - for (const correlationGroupJson of intraTileCorrelationGroupsJson) { - const groupFlags = correlationGroupJson.groupFlags; - const rotationThetas = Cartesian3.fromArray( - correlationGroupJson.rotationThetas, - 0, - new Cartesian3() - ); - const params = []; - for (const paramJson of correlationGroupJson.params) { - const param = new Spdcf({ - A: paramJson.A, - alpha: paramJson.alpha, - beta: paramJson.beta, - T: paramJson.T, - }); - params.push(param); - } - const correlationGroup = new CorrelationGroup({ - groupFlags: groupFlags, - rotationThetas: rotationThetas, - params: params, - }); - intraTileCorrelationGroups.push(correlationGroup); - } - - this._gltfGpmLocal = new GltfGpmLocal({ - storageType: storageType, - anchorPointsIndirect: anchorPointsIndirect, - intraTileCorrelationGroups: intraTileCorrelationGroups, - }); - this._state = ResourceLoaderState.READY; - return true; + for (const correlationGroupJson of intraTileCorrelationGroupsJson) { + const correlationGroup = createCorrelationGroup(correlationGroupJson); + intraTileCorrelationGroups.push(correlationGroup); } - this._state = ResourceLoaderState.FAILED; - return false; -}; -/** - * Unloads the resource. - * @private - */ -GltfGpmLoader.prototype.unload = function () { - this._gltfGpmLocal = undefined; + const gltfGpmLocal = new GltfGpmLocal({ + storageType: StorageType.Indirect, + anchorPointsIndirect: anchorPointsIndirect, + intraTileCorrelationGroups: intraTileCorrelationGroups, + }); + return gltfGpmLocal; }; export default GltfGpmLoader; From 1cc38faedcf3772c804589a05150182d43a2fee1 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 21 Sep 2024 17:28:06 +0200 Subject: [PATCH 09/38] Minor cleanup and comments --- .../Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index c97ae55d7c6..485caae1f80 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -246,10 +246,10 @@ GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( // Given that `offset` and `scale` may only be applied to integer // property values when they are `normalized`, the values will be // declared as `normalized` here. - // The and the normalization factor will later have to be - // cancelled out, when integrating the `scale` into the - // actual property texture property. In the property texture - // property, the `scale` has to be multiplied by 255. + // The normalization factor will later have to be cancelled out, + // when integrating the `scale` into the actual property texture + // property. In the property texture property, the `scale` has to + // be multiplied by 255. const classJson = { name: `PPE texture class ${index}`, properties: { @@ -410,7 +410,7 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { // that `offset` and `scale` can be applied. The normalization // factor has to be cancelled out here, by multiplying the // `scale` with 255. - const scale = ppeTexture.scale * 255.0; + const scale = (ppeTexture.scale ?? 1.0) * 255.0; const ppeTextureAsPropertyTexture = { class: classId, properties: { @@ -465,6 +465,8 @@ function unloadTextures(meshPrimitiveGpmLoader) { */ GltfMeshPrimitiveGpmLoader.prototype.unload = function () { unloadTextures(this); + this._gltf = undefined; + this._extension = undefined; this._structuralMetadata = undefined; }; From 4e6d501af04672d5d339765049236877574a45fa Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 21 Sep 2024 18:06:28 +0200 Subject: [PATCH 10/38] Fix type checks --- .../engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js | 2 +- packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index de255f5b6dd..66d38ef40cb 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -8,7 +8,7 @@ import Check from "../../../../Core/Check.js"; */ function PpeMetadata(options) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.source", options.source); + Check.typeOf.string("options.source", options.source); //>>includeEnd('debug'); this._min = options.min; diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 5214a6e34ac..8b352956758 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -13,7 +13,7 @@ import Check from "../../../../Core/Check.js"; function PpeTexture(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.traits", options.traits); - Check.typeOf.object("options.index", options.index); + Check.typeOf.number.greaterThanOrEquals("options.index", options.index, 0); //>>includeEnd('debug'); this._traits = options.traits; From 9e7bfcf0aee24f1d84b23ac57fba20ab1a23ad28 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 21 Sep 2024 18:06:50 +0200 Subject: [PATCH 11/38] Explicit conversion of PPE into structural metadata --- .../Gpm/GltfMeshPrimitiveGpmLoader.js | 288 +++++++++++------- 1 file changed, 177 insertions(+), 111 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index 485caae1f80..e8d52cb7299 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -7,18 +7,20 @@ import ResourceLoaderState from "../../../ResourceLoaderState.js"; import PropertyTexture from "../../../PropertyTexture.js"; import StructuralMetadata from "../../../StructuralMetadata.js"; import MetadataSchema from "../../../MetadataSchema.js"; +import PpeTexture from "./PpeTexture.js"; +import PpeMetadata from "./PpeMetadata.js"; +import MeshPrimitiveGpmLocal from "./MeshPrimitiveGpmLocal.js"; /** * Loads glTF NGA_gpm_local from a glTF mesh primitive. *

* Implements the {@link ResourceLoader} interface. *

- * Implementation note: This is an experimental implementation. It is based - * on a GltfStructuralMetadataLoader, by removing everything that is not - * related to property textures, and translating the "ppeTextures" of - * the NGA_gpm_local extension into property texture properties. These will - * be returned as part of a `StructuralMetadata` object, which may override - * any `StructuralMetadata` that was read directly from the glTF. + * This loads the "ppeTextures" of the NGA_gpm_local extension of a mesh primitive + * and stores them in a `MeshPrimitiveGpmLocal` object. + * + * This object will be converted into a `StructuralMetadata` object, which may + * override any `StructuralMetadata` that was read directly from the glTF. * * @alias GltfMeshPrimitiveGpmLoader * @constructor @@ -67,6 +69,7 @@ function GltfMeshPrimitiveGpmLoader(options) { this._asynchronous = asynchronous; this._textureLoaders = []; this._textureIds = []; + this._meshPrimitiveGpmLocal = undefined; this._structuralMetadata = undefined; this._state = ResourceLoaderState.UNLOADED; this._promise = undefined; @@ -110,30 +113,30 @@ Object.defineProperties(GltfMeshPrimitiveGpmLoader.prototype, { }, }); -async function loadResources(loader) { +GltfMeshPrimitiveGpmLoader.prototype.loadResources = async function () { try { - const texturesPromise = loadTextures(loader); + const texturesPromise = this.loadTextures(); await texturesPromise; - if (loader.isDestroyed()) { + if (this.isDestroyed()) { return; } - loader._gltf = undefined; // No longer need to hold onto the glTF + this._gltf = undefined; // No longer need to hold onto the glTF - loader._state = ResourceLoaderState.LOADED; - return loader; + this._state = ResourceLoaderState.LOADED; + return this; } catch (error) { - if (loader.isDestroyed()) { + if (this.isDestroyed()) { return; } - loader.unload(); - loader._state = ResourceLoaderState.FAILED; + this.unload(); + this._state = ResourceLoaderState.FAILED; const errorMessage = "Failed to load GPM data"; - throw loader.getError(errorMessage, error); + throw this.getError(errorMessage, error); } -} +}; /** * Loads the resource. @@ -146,7 +149,7 @@ GltfMeshPrimitiveGpmLoader.prototype.load = function () { } this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); + this._promise = this.loadResources(this); return this._promise; }; @@ -164,18 +167,18 @@ function gatherUsedTextureIds(gpmExtension) { return textureIds; } -function loadTextures(meshPrimitiveGpmLoader) { +GltfMeshPrimitiveGpmLoader.prototype.loadTextures = function () { let textureIds; - if (defined(meshPrimitiveGpmLoader._extension)) { - textureIds = gatherUsedTextureIds(meshPrimitiveGpmLoader._extension); + if (defined(this._extension)) { + textureIds = gatherUsedTextureIds(this._extension); } - const gltf = meshPrimitiveGpmLoader._gltf; - const gltfResource = meshPrimitiveGpmLoader._gltfResource; - const baseResource = meshPrimitiveGpmLoader._baseResource; - const supportedImageFormats = meshPrimitiveGpmLoader._supportedImageFormats; - const frameState = meshPrimitiveGpmLoader._frameState; - const asynchronous = meshPrimitiveGpmLoader._asynchronous; + const gltf = this._gltf; + const gltfResource = this._gltfResource; + const baseResource = this._baseResource; + const supportedImageFormats = this._supportedImageFormats; + const frameState = this._frameState; + const asynchronous = this._asynchronous; // Load the textures const texturePromises = []; @@ -190,14 +193,14 @@ function loadTextures(meshPrimitiveGpmLoader) { frameState: frameState, asynchronous: asynchronous, }); - meshPrimitiveGpmLoader._textureLoaders.push(textureLoader); - meshPrimitiveGpmLoader._textureIds.push(textureId); + this._textureLoaders.push(textureLoader); + this._textureIds.push(textureId); texturePromises.push(textureLoader.load()); } } return Promise.all(texturePromises); -} +}; /** * A static mapping from PPE texture property identifier keys @@ -217,7 +220,7 @@ GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); * @param {number} index - The index of the texture in the extension * @returns The class JSON */ -GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( +GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson = function ( ppeTexture, index ) { @@ -267,18 +270,24 @@ GltfMeshPrimitiveGpmLoader.prototype.createPpeTextureClassJson = function ( }; /** - * Returns the `MetadataSchema` for the PPE textures in this instance. + * Returns the `MetadataSchema` for the PPE textures in the given + * `MeshPrimitiveGpmLocal` instance. * * This method will return a (statically/globally) cached metadata - * schema that reflects the structure of the PPE textures in this - * instance, creating and caching it if necessary. + * schema that reflects the structure of the PPE textures in the + * given instance, creating and caching it if necessary. * * For details on the cache key, see `collectPpeTexturePropertyIdentifiers` * + * @param {MeshPrimitiveGpmLocal} meshPrimitiveGpmLocal The extension object * @returns The `MetadataSchema` */ -GltfMeshPrimitiveGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function () { - const ppeTexturePropertyIdentifiers = this.collectPpeTexturePropertyIdentifiers(); +GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( + meshPrimitiveGpmLocal +) { + const ppeTexturePropertyIdentifiers = GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers( + meshPrimitiveGpmLocal + ); const key = ppeTexturePropertyIdentifiers.toString(); let ppeTexturesMetadataSchema = GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache.get( key @@ -297,14 +306,15 @@ GltfMeshPrimitiveGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function classes: {}, }; - const extension = this._extension; - if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - const classId = `ppeTexture_${i}`; - const classJson = this.createPpeTextureClassJson(ppeTexture, i); - ppeTexturesMetadataSchemaJson.classes[classId] = classJson; - } + const ppeTextures = meshPrimitiveGpmLocal.ppeTextures; + for (let i = 0; i < ppeTextures.length; i++) { + const ppeTexture = ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const classJson = GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson( + ppeTexture, + i + ); + ppeTexturesMetadataSchemaJson.classes[classId] = classJson; } ppeTexturesMetadataSchema = MetadataSchema.fromJson( @@ -329,26 +339,98 @@ GltfMeshPrimitiveGpmLoader.prototype.obtainPpeTexturesMetadataSchema = function * that are relevant for distinguishing two PPE textures in terms * of their structure within a `StructuralMetadata`. * + * @param {MeshPrimitiveGpmLocal} meshPrimitiveGpmLocal The extension object * @returns The identifiers */ -GltfMeshPrimitiveGpmLoader.prototype.collectPpeTexturePropertyIdentifiers = function () { - const extension = this._extension; +GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers = function ( + meshPrimitiveGpmLocal +) { const ppeTexturePropertyIdentifiers = []; - if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - // The following will create an identifier that can be used - // to define two PPE textures as "representing the same - // property texture property" within a structural metadata - // schema. - const classJson = this.createPpeTextureClassJson(ppeTexture, i); - const ppeTexturePropertyIdentifier = JSON.stringify(classJson); - ppeTexturePropertyIdentifiers.push(ppeTexturePropertyIdentifier); - } + const ppeTextures = meshPrimitiveGpmLocal.ppeTextures; + for (let i = 0; i < ppeTextures.length; i++) { + const ppeTexture = ppeTextures[i]; + // The following will create an identifier that can be used + // to define two PPE textures as "representing the same + // property texture property" within a structural metadata + // schema. + const classJson = GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson( + ppeTexture, + i + ); + const ppeTexturePropertyIdentifier = JSON.stringify(classJson); + ppeTexturePropertyIdentifiers.push(ppeTexturePropertyIdentifier); } return ppeTexturePropertyIdentifiers; }; +/** + * Converts the given `MeshPrimitiveGpmLocal` object into a `StructuralMetadata` + * object. + * + * This will translate the PPE textures from the given object into property + * texture properties. The schema will be created based on the the structure + * of the PPE textures. + * + * @param {MeshPrimitiveGpmLocal} meshPrimitiveGpmLocal The extension object + * @param {object} textures The mapping from texture ID to texture objects + * @returns The `StructuralMetadata` object + */ +GltfMeshPrimitiveGpmLoader.convertToStructuralMetadata = function ( + meshPrimitiveGpmLocal, + textures +) { + const propertyTextures = []; + const ppeTexturesMetadataSchema = GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema( + meshPrimitiveGpmLocal + ); + const ppeTextures = meshPrimitiveGpmLocal.ppeTextures; + for (let i = 0; i < ppeTextures.length; i++) { + const ppeTexture = ppeTextures[i]; + const classId = `ppeTexture_${i}`; + const traits = ppeTexture.traits; + const ppePropertyName = traits.source; + const metadataClass = ppeTexturesMetadataSchema.classes[classId]; + + // XXX_UNCERTAINTY Debug log + //console.log( + // `Creating property texture with class ${classId} and property ${ppePropertyName}` + //); + + // The class property has been declared as `normalized`, so + // that `offset` and `scale` can be applied. The normalization + // factor has to be cancelled out here, by multiplying the + // `scale` with 255. + const scale = (ppeTexture.scale ?? 1.0) * 255.0; + const ppeTextureAsPropertyTexture = { + class: classId, + properties: { + [ppePropertyName]: { + index: ppeTexture.index, + texCoord: ppeTexture.texCoord, + offset: ppeTexture.offset, + scale: scale, + }, + }, + }; + propertyTextures.push( + new PropertyTexture({ + id: i, + name: ppeTexture.name, + propertyTexture: ppeTextureAsPropertyTexture, + class: metadataClass, + textures: textures, + }) + ); + } + const structuralMetadata = new StructuralMetadata({ + schema: ppeTexturesMetadataSchema, + propertyTables: [], + propertyTextures: propertyTextures, + propertyAttributes: [], + }); + return structuralMetadata; +}; + /** * Processes the resource until it becomes ready. * @@ -368,6 +450,8 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { return false; } + // The standard process of loading textures + // (from GltfStructuralMetadataLoader) const textureLoaders = this._textureLoaders; const textureLoadersLength = textureLoaders.length; let ready = true; @@ -381,6 +465,8 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { return false; } + // More of the standard process of loading textures + // (from GltfStructuralMetadataLoader) const textures = {}; for (let i = 0; i < this._textureIds.length; ++i) { const textureId = this._textureIds[i]; @@ -390,81 +476,61 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { } } - const ppeTexturesMetadataSchema = this.obtainPpeTexturesMetadataSchema(); + // Convert the JSON representation of the `ppeTextures` that + // are found in the extensjon JSON into `PpeTexture` objects + const ppeTextures = []; const extension = this._extension; - const propertyTextures = []; if (defined(extension.ppeTextures)) { - for (let i = 0; i < extension.ppeTextures.length; i++) { - const ppeTexture = extension.ppeTextures[i]; - const classId = `ppeTexture_${i}`; - const traits = ppeTexture.traits; - const ppePropertyName = traits.source; - const metadataClass = ppeTexturesMetadataSchema.classes[classId]; - - // XXX_UNCERTAINTY Debug log - //console.log( - // `Creating property texture with class ${classId} and property ${ppePropertyName}` - //); - - // The class property has been declared as `normalized`, so - // that `offset` and `scale` can be applied. The normalization - // factor has to be cancelled out here, by multiplying the - // `scale` with 255. - const scale = (ppeTexture.scale ?? 1.0) * 255.0; - const ppeTextureAsPropertyTexture = { - class: classId, - properties: { - [ppePropertyName]: { - index: ppeTexture.index, - texCoord: ppeTexture.texCoord, - offset: ppeTexture.offset, - scale: scale, - }, - }, - }; - propertyTextures.push( - new PropertyTexture({ - id: i, - name: ppeTexture.name, - propertyTexture: ppeTextureAsPropertyTexture, - class: metadataClass, - textures: textures, - }) - ); + const ppeTexturesJson = extension.ppeTextures; + for (const ppeTextureJson of ppeTexturesJson) { + const traitsJson = ppeTextureJson.traits; + const traits = new PpeMetadata({ + min: traitsJson.min, + max: traitsJson.max, + source: traitsJson.source, + }); + const ppeTexture = new PpeTexture({ + traits: traits, + noData: ppeTextureJson.noData, + offset: ppeTextureJson.offset, + scale: ppeTextureJson.scale, + index: ppeTextureJson.index, + texCoord: ppeTextureJson.texCoord, + }); + ppeTextures.push(ppeTexture); } } - - const structuralMetadata = new StructuralMetadata({ - schema: ppeTexturesMetadataSchema, - propertyTables: [], - propertyTextures: propertyTextures, - propertyAttributes: [], - statistics: extension.statistics, - extras: extension.extras, - extensions: extension.extensions, + const meshPrimitiveGpmLocal = new MeshPrimitiveGpmLocal({ + ppeTextures: ppeTextures, }); + this._meshPrimitiveGpmLocal = meshPrimitiveGpmLocal; + + const structuralMetadata = GltfMeshPrimitiveGpmLoader.convertToStructuralMetadata( + meshPrimitiveGpmLocal, + textures + ); this._structuralMetadata = structuralMetadata; this._state = ResourceLoaderState.READY; return true; }; -function unloadTextures(meshPrimitiveGpmLoader) { - const textureLoaders = meshPrimitiveGpmLoader._textureLoaders; +GltfMeshPrimitiveGpmLoader.prototype.unloadTextures = function () { + const textureLoaders = this._textureLoaders; const textureLoadersLength = textureLoaders.length; for (let i = 0; i < textureLoadersLength; ++i) { ResourceCache.unload(textureLoaders[i]); } - meshPrimitiveGpmLoader._textureLoaders.length = 0; - meshPrimitiveGpmLoader._textureIds.length = 0; -} + this._textureLoaders.length = 0; + this._textureIds.length = 0; +}; /** * Unloads the resource. * @private */ GltfMeshPrimitiveGpmLoader.prototype.unload = function () { - unloadTextures(this); + this.unloadTextures(); this._gltf = undefined; this._extension = undefined; this._structuralMetadata = undefined; From 5110166947f0cd46c7be4fdb37fb9a006bdfd95f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 24 Sep 2024 22:36:46 +0200 Subject: [PATCH 12/38] Remove debug logs --- packages/engine/Source/Scene/GltfLoader.js | 11 +++++++---- .../Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js | 9 --------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index d7abc6d3fee..c6733177bdd 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -31,6 +31,7 @@ import SupportedImageFormats from "./SupportedImageFormats.js"; import VertexAttributeSemantic from "./VertexAttributeSemantic.js"; import GltfGpmLoader from "./Model/Extensions/Gpm/GltfGpmLoader.js"; import GltfMeshPrimitiveGpmLoader from "./Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js"; +import oneTimeWarning from "../Core/oneTimeWarning.js"; const { Attribute, @@ -478,8 +479,12 @@ function processLoaders(loader, frameState) { if (defined(meshPrimitiveGpmLoader)) { const metadataReady = meshPrimitiveGpmLoader.process(frameState); if (metadataReady) { - // XXX - console.log("Overwriting structural metadata with GPM data"); + if (defined(loader._components.structuralMetadata)) { + oneTimeWarning( + "structural-metadata-gpm", + "Structural metadata is replaced with GPM data" + ); + } loader._components.structuralMetadata = meshPrimitiveGpmLoader.structuralMetadata; } @@ -2728,8 +2733,6 @@ function parse(loader, frameState) { const primitiveExtensions = primitive.extensions; const meshPrimitiveGpmExtension = primitiveExtensions.NGA_gpm_local; if (defined(meshPrimitiveGpmExtension)) { - // XXX - console.log("Loading GPM from mesh primitive"); const promise = loadMeshPrimitiveGpm( loader, gltf, diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index e8d52cb7299..c207a962a70 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -293,13 +293,9 @@ GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( key ); if (defined(ppeTexturesMetadataSchema)) { - // XXX_UNCERTAINTY Debug log - //console.log(`Using cached schema for GPM PPE textures with key ${key}`); return ppeTexturesMetadataSchema; } - // XXX_UNCERTAINTY Debug log - if caching works, this should be printed only ONCE! - console.log(`Creating schema for GPM PPE textures with key ${key}`); const schemaId = `PPE_TEXTURE_SCHEMA_${GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache.size}`; const ppeTexturesMetadataSchemaJson = { id: schemaId, @@ -391,11 +387,6 @@ GltfMeshPrimitiveGpmLoader.convertToStructuralMetadata = function ( const ppePropertyName = traits.source; const metadataClass = ppeTexturesMetadataSchema.classes[classId]; - // XXX_UNCERTAINTY Debug log - //console.log( - // `Creating property texture with class ${classId} and property ${ppePropertyName}` - //); - // The class property has been declared as `normalized`, so // that `offset` and `scale` can be applied. The normalization // factor has to be cancelled out here, by multiplying the From 323f4f01afff34610875ea433c5705668ec1061e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 24 Sep 2024 22:36:59 +0200 Subject: [PATCH 13/38] Additional type checks --- .../Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index c3a1a0c0bbb..730aca14399 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -172,6 +172,14 @@ GltfGpmLoader.load = function (gltfGpmLocalJson) { GltfGpmLoader.loadDirect = function (gltfGpmLocalJson) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("gltfGpmLocalJson", gltfGpmLocalJson); + Check.typeOf.object( + "gltfGpmLocalJson.anchorPointsDirect", + gltfGpmLocalJson.anchorPointsDirect + ); + Check.typeOf.object( + "gltfGpmLocalJson.covarianceDirectUpperTriangle", + gltfGpmLocalJson.covarianceDirectUpperTriangle + ); //>>includeEnd('debug'); const anchorPointsDirect = []; @@ -205,6 +213,14 @@ GltfGpmLoader.loadDirect = function (gltfGpmLocalJson) { GltfGpmLoader.loadIndirect = function (gltfGpmLocalJson) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("gltfGpmLocalJson", gltfGpmLocalJson); + Check.typeOf.object( + "gltfGpmLocalJson.anchorPointsIndirect", + gltfGpmLocalJson.anchorPointsIndirect + ); + Check.typeOf.object( + "gltfGpmLocalJson.intraTileCorrelationGroups", + gltfGpmLocalJson.intraTileCorrelationGroups + ); //>>includeEnd('debug'); const anchorPointsIndirect = []; From 6326d40261ce405f5f6c4fe6a0bd901f1d3b988f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 24 Sep 2024 22:37:18 +0200 Subject: [PATCH 14/38] Added JSDoc comment --- packages/engine/Source/Scene/Model/Model.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index 7d481d2088d..c68109d6093 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -1773,9 +1773,20 @@ Model.prototype.applyArticulations = function () { }; /** - * XXX_TODO_COMMENT + * Returns the object that was created for the given extension. * + * The given name may be the name of a glTF extension, like `"EXT_example_extension"`. + * If the specified extension was present in the root of the underlying glTF asset, + * and a loder for the specified extension has processed the extension data, then + * this will return the model representation of the extension. + * + * @param {string} extensionName The name of the extension + * @returns The object, or `undefined` * @exception {DeveloperError} The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true. + * + * @private + * @experimental This feature is subject to change without Cesium's standard deprecation policy. + * */ Model.prototype.getExtension = function (extensionName) { //>>includeStart('debug', pragmas.debug); From eaf6112903870dc7553275d9ec9574a7b65303e4 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 24 Sep 2024 22:37:56 +0200 Subject: [PATCH 15/38] First part of GPM loading specs --- .../Model/Extensions/Gpm/GltfGpmLoaderSpec.js | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js diff --git a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js new file mode 100644 index 00000000000..c8920fb2831 --- /dev/null +++ b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js @@ -0,0 +1,201 @@ +import Cartesian3 from "../../../../../Source/Core/Cartesian3.js"; +import CesiumMath from "../../../../../Source/Core/Math.js"; +import Matrix3 from "../../../../../Source/Core/Matrix3.js"; +import GltfGpmLoader from "../../../../../Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js"; + +describe("Scene/Model/Extensions/Gpm/GltfGpmLoader", function () { + it("load throws with invalid storageType", async function () { + const gltfGpmLocalJson = { + storageType: "INVALID", + }; + expect(function () { + GltfGpmLoader.load(gltfGpmLocalJson); + }).toThrowDeveloperError(); + }); + + it("load throws for storageType Direct without anchorPointsDirect", async function () { + const gltfGpmLocalJson = { + storageType: "Direct", + }; + expect(function () { + GltfGpmLoader.load(gltfGpmLocalJson); + }).toThrowDeveloperError(); + }); + + it("load throws for storageType Direct without covarianceDirectUpperTriangle", async function () { + const gltfGpmLocalJson = { + storageType: "Direct", + anchorPointsDirect: [ + { + position: [1.0, 2.0, 3.0], + adjustmentParams: [0.1, 0.2, 0.3], + }, + ], + }; + expect(function () { + GltfGpmLoader.load(gltfGpmLocalJson); + }).toThrowDeveloperError(); + }); + + it("load returns result for valid JSON for storageType Direct", async function () { + const gltfGpmLocalJson = { + storageType: "Direct", + anchorPointsDirect: [ + { + position: [1.0, 2.0, 3.0], + adjustmentParams: [0.1, 0.2, 0.3], + }, + ], + covarianceDirectUpperTriangle: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6], + }; + + const result = GltfGpmLoader.load(gltfGpmLocalJson); + expect(result).toBeDefined(); + expect(result.anchorPointsDirect.length).toBe(1); + + const actualAnchorPoint = result.anchorPointsDirect[0]; + + const expectedPosition = new Cartesian3(1.0, 2.0, 3.0); + expect( + Cartesian3.equalsEpsilon( + actualAnchorPoint.position, + expectedPosition, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + const expectedAdjustmentParams = new Cartesian3(0.1, 0.2, 0.3); + expect( + Cartesian3.equalsEpsilon( + actualAnchorPoint.adjustmentParams, + expectedAdjustmentParams, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + const expectedCovarianceDirect = Matrix3.fromArray( + [0.1, 0.2, 0.4, 0.2, 0.3, 0.5, 0.4, 0.5, 0.6], + 0, + new Matrix3() + ); + expect( + Matrix3.equalsEpsilon( + result.covarianceDirect, + expectedCovarianceDirect, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + }); + + it("load throws for storageType Indirect without anchorPointsIndirect", async function () { + const gltfGpmLocalJson = { + storageType: "Indirect", + }; + expect(function () { + GltfGpmLoader.load(gltfGpmLocalJson); + }).toThrowDeveloperError(); + }); + + it("load throws for storageType Indirect without intraTileCorrelationGroups", async function () { + const gltfGpmLocalJson = { + storageType: "Indirect", + anchorPointsIndirect: [ + { + position: [1.0, 2.0, 3.0], + adjustmentParams: [0.1, 0.2, 0.3], + }, + ], + }; + expect(function () { + GltfGpmLoader.load(gltfGpmLocalJson); + }).toThrowDeveloperError(); + }); + + it("load returns result for valid JSON for storageType Indirect", async function () { + const gltfGpmLocalJson = { + storageType: "Indirect", + anchorPointsIndirect: [ + { + position: [1.0, 2.0, 3.0], + adjustmentParams: [0.1, 0.2, 0.3], + covarianceMatrix: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6], + }, + ], + intraTileCorrelationGroups: [ + { + groupFlags: [true, true, true], + rotationThetas: [0.1, 0.2, 0.3], + params: [ + { + A: 0.1, + alpha: 0.2, + beta: 0.3, + T: 0.4, + }, + ], + }, + ], + }; + + const result = GltfGpmLoader.load(gltfGpmLocalJson); + expect(result).toBeDefined(); + + expect(result.anchorPointsIndirect.length).toBe(1); + + const actualAnchorPoint = result.anchorPointsIndirect[0]; + + const expectedPosition = new Cartesian3(1.0, 2.0, 3.0); + expect( + Cartesian3.equalsEpsilon( + actualAnchorPoint.position, + expectedPosition, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + const expectedAdjustmentParams = new Cartesian3(0.1, 0.2, 0.3); + expect( + Cartesian3.equalsEpsilon( + actualAnchorPoint.adjustmentParams, + expectedAdjustmentParams, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + const expectedCovarianceMatrix = Matrix3.fromArray( + [0.1, 0.2, 0.4, 0.2, 0.3, 0.5, 0.4, 0.5, 0.6], + 0, + new Matrix3() + ); + expect( + Matrix3.equalsEpsilon( + actualAnchorPoint.covarianceMatrix, + expectedCovarianceMatrix, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + expect(result.intraTileCorrelationGroups.length).toBe(1); + + const correlationGroup = result.intraTileCorrelationGroups[0]; + const groupFlags = correlationGroup.groupFlags; + expect(groupFlags).toEqual([true, true, true]); + + const expectedRotationThetas = new Cartesian3(0.1, 0.2, 0.3); + expect( + Cartesian3.equalsEpsilon( + correlationGroup.rotationThetas, + expectedRotationThetas, + CesiumMath.EPSILON6 + ) + ).toBeTrue(); + + const params = correlationGroup.params; + expect(params.length).toBe(1); + const param = params[0]; + expect(param.A).toBe(0.1); + expect(param.alpha).toBe(0.2); + expect(param.beta).toBe(0.3); + expect(param.T).toBe(0.4); + }); +}); From 9dbd45ec7029e62cbe01e829c2070ab6c0b1ed3c Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 25 Sep 2024 15:02:46 +0200 Subject: [PATCH 16/38] Proper imports for spec file --- .../Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js index c8920fb2831..b4381d5aad9 100644 --- a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js +++ b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js @@ -1,7 +1,9 @@ -import Cartesian3 from "../../../../../Source/Core/Cartesian3.js"; -import CesiumMath from "../../../../../Source/Core/Math.js"; -import Matrix3 from "../../../../../Source/Core/Matrix3.js"; -import GltfGpmLoader from "../../../../../Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js"; +import { + Matrix3, + Math as CesiumMath, + Cartesian3, + GltfGpmLoader, +} from "../../../../../index.js"; describe("Scene/Model/Extensions/Gpm/GltfGpmLoader", function () { it("load throws with invalid storageType", async function () { From 6e290c8a84101a83982ee6d4ada11664f2ed3e8e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 25 Sep 2024 15:03:07 +0200 Subject: [PATCH 17/38] Explicitly offer parsed GPM data --- .../Gpm/GltfMeshPrimitiveGpmLoader.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index c207a962a70..9fc96bb907e 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -97,8 +97,27 @@ Object.defineProperties(GltfMeshPrimitiveGpmLoader.prototype, { return this._cacheKey; }, }, + /** - * The parsed structural metadata + * The parsed GPM extension information from the mesh primitive + * + * @memberof GltfMeshPrimitiveGpmLoader.prototype + * + * @type {MeshPrimitiveGpmLocal} + * @readonly + * @private + */ + meshPrimitiveGpmLocal: { + get: function () { + return this._meshPrimitiveGpmLocal; + }, + }, + + /** + * Returns the result of converting the parsed 'MeshPrimitiveGpmLocal' + * into a 'StructuralMetadata'. + * + * Some details about the translation are intentionally not specified here. * * @memberof GltfMeshPrimitiveGpmLoader.prototype * From 6e41b365ba55fb875da9e5c5e1563ec6db22b8ac Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 25 Sep 2024 15:03:26 +0200 Subject: [PATCH 18/38] Add specs for mesh primitive GPM data --- .../Gpm/GltfMeshPrimitiveGpmLoaderSpec.js | 429 ++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoaderSpec.js diff --git a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoaderSpec.js b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoaderSpec.js new file mode 100644 index 00000000000..81591889fbd --- /dev/null +++ b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoaderSpec.js @@ -0,0 +1,429 @@ +import { + GltfTextureLoader, + Resource, + ResourceCache, + SupportedImageFormats, + defined, + GltfMeshPrimitiveGpmLoader, +} from "../../../../../index.js"; +import createScene from "../../../../../../../Specs/createScene.js"; +import loaderProcess from "../../../../../../../Specs/loaderProcess.js"; +import waitForLoaderProcess from "../../../../../../../Specs/waitForLoaderProcess.js"; + +/** + * The JSON representation of the NGA_gpm_local extension object + * that will be inserted into the mesh primitive + */ +const ngaGpmLocalExtension = { + ppeTextures: [ + { + traits: { + source: "SIGZ", + min: 0.0, + max: 16.0, + }, + index: 0, + noData: 255, + offset: 0.0, + scale: 0.06274509803921569, + texCoord: 0, + }, + ], +}; + +/** + * Creates an embedded glTF with a single mesh primitive. + * + * The mesh primitive is a single unit square with normals and texture + * coordinates in [(0,0)-(1.1)]. The glTF defines a single texture, + * with an image that is stored as an embdedded 16x16 PNG file + * where the red channel contains values in [0,256] (and the alpha + * channels contains 255). + * + * If the given 'gpmExtension' object is defined, then it will be + * inserted as the "NGA_gpm_local" extension in the mesh primitive, + * and "NGA_gpm_local" will be added to the 'extensionsUsed'. + * + * @param {object} gpmExtension The NGA_gpm_local extension JSON object + * @returns The glTF + */ +function createEmbeddedGltf(gpmExtension) { + let meshPrimitiveExtensions; + let extensionsUsed; + + if (defined(gpmExtension)) { + meshPrimitiveExtensions = { + NGA_gpm_local: gpmExtension, + }; + extensionsUsed = ["NGA_gpm_local"]; + } + + const gltf = { + accessors: [ + { + bufferView: 0, + byteOffset: 0, + componentType: 5123, + count: 6, + type: "SCALAR", + max: [3], + min: [0], + }, + { + bufferView: 1, + byteOffset: 0, + componentType: 5126, + count: 4, + type: "VEC3", + max: [1.0, 1.0, 0.0], + min: [0.0, 0.0, 0.0], + }, + { + bufferView: 1, + byteOffset: 48, + componentType: 5126, + count: 4, + type: "VEC3", + max: [0.0, 0.0, 1.0], + min: [0.0, 0.0, 1.0], + }, + { + bufferView: 1, + byteOffset: 96, + componentType: 5126, + count: 4, + type: "VEC2", + max: [1.0, 1.0], + min: [0.0, 0.0], + }, + ], + asset: { + generator: "JglTF from https://github.com/javagl/JglTF", + version: "2.0", + }, + buffers: [ + { + uri: + "data:application/gltf-buffer;base64,AAABAAIAAQADAAIAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAA", + byteLength: 156, + }, + ], + bufferViews: [ + { + buffer: 0, + byteOffset: 0, + byteLength: 12, + target: 34963, + }, + { + buffer: 0, + byteOffset: 12, + byteLength: 144, + byteStride: 12, + target: 34962, + }, + ], + images: [ + { + uri: + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABs0lEQVR42hXMUwCWBwBA0b8Wlo1lLHNt2a4tLZtbrdZatm1jWcu2bSxrtWq1Zds438O5jzcUCoU+hSEsXxCO8EQgIl8SichEISrRiE4MQjElFrGJQ1ziEZ8EJOQrEpGYJCQlGcmDQQpJSSpS8zVpSEs60pOBjGQiM1nISrZgkF2+IQff8h05yUVu8pCXfOSnAAUpROFgUESKUozilKAkpShNGcryPT9QjvJUoGIwqCQ/UpkqVKUa1alBTWpRmzrUpR71aRAMGkojGtOEn/iZpjTjF5rTgl9pyW+04vdg0Fra0JZ2tKcDHelEZ7rQlW50pwc96RUMeksf+tKP/gxgIIMYzBCGMozhjGAko4LBaBnDWMYxnglMZBJ/MJkpTGUa05nBzGAwS2Yzh7n8yTzms4CFLGIxS1jKMpazIhislFWsZg1rWcd6NrCRTWxmC1vZxnZ2BIOdsovd7GEv+9jPAQ5yiMMc4Sh/cYzjweCEnOQUpznDWc5xngv8zUUu8Q+XucLVYPCvXOM6//E/N7jJLW5zh7vc4z4PeMijYPBYnvCUZzznBS95xWve8JZ3vOcDH/nEZ7gvfpBCxLDKAAAAAElFTkSuQmCC", + mimeType: "image/png", + }, + ], + meshes: [ + { + primitives: [ + { + attributes: { + POSITION: 1, + NORMAL: 2, + TEXCOORD_0: 3, + }, + indices: 0, + mode: 4, + extensions: meshPrimitiveExtensions, + }, + ], + }, + ], + nodes: [ + { + mesh: 0, + }, + ], + samplers: [ + { + magFilter: 9728, + minFilter: 9728, + wrapS: 33071, + wrapT: 33071, + }, + ], + scene: 0, + scenes: [ + { + nodes: [0], + }, + ], + textures: [ + { + sampler: 0, + source: 0, + }, + ], + extensionsUsed: extensionsUsed, + }; + return gltf; +} + +// NOTE: Much of this was taken from 'GltfSructuralMetadataLoaderSpec.js'. +// I don't know what things like the 'mockFrameState' are. +// I don't know how much of the 'Resource'-specific testing belongs here. +// I don't know what 'resolveAfterDestroy' is actually testing. +// Otherwise, I would have added comments... +describe( + "Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader", + function () { + const gltfUri = "https://example.com/model.glb"; + const gltfResource = new Resource({ + url: gltfUri, + }); + + const mockFrameState = { + context: { + id: "01234", + }, + }; + + let scene; + + beforeAll(function () { + scene = createScene(); + }); + + afterAll(function () { + scene.destroyForSpecs(); + }); + + afterEach(function () { + ResourceCache.clearForSpecs(); + }); + + it("throws if gltf is undefined", function () { + const extension = undefined; + const gltf = undefined; + + expect(function () { + return new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + }).toThrowDeveloperError(); + }); + + it("throws if gltfResource is undefined", function () { + const extension = undefined; + const gltf = undefined; + + expect(function () { + return new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: undefined, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + }).toThrowDeveloperError(); + }); + + it("throws if baseResource is undefined", function () { + const extension = undefined; + const gltf = undefined; + + expect(function () { + return new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: undefined, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + }).toThrowDeveloperError(); + }); + + it("throws if supportedImageFormats is undefined", function () { + const extension = undefined; + const gltf = undefined; + + expect(function () { + return new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: undefined, + frameState: mockFrameState, + }); + }).toThrowDeveloperError(); + }); + + it("throws if frameState is undefined", function () { + const extension = undefined; + const gltf = undefined; + + expect(function () { + return new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: undefined, + }); + }).toThrowDeveloperError(); + }); + + it("loads mesh primitive GPM extension data", async function () { + const extension = ngaGpmLocalExtension; + const gltf = createEmbeddedGltf(extension); + + const loader = new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + + await loader.load(); + await waitForLoaderProcess(loader, scene); + expect(() => loaderProcess(loader, scene)).not.toThrow(); + + const gpmData = loader.meshPrimitiveGpmLocal; + expect(gpmData).toBeDefined(); + + const ppeTextures = gpmData.ppeTextures; + expect(ppeTextures).toBeDefined(); + expect(ppeTextures.length).toBe(1); + + const ppeTexture = ppeTextures[0]; + expect(ppeTexture.index).toBe(0); + expect(ppeTexture.texCoord).toBe(0); + expect(ppeTexture.noData).toBe(255); + expect(ppeTexture.offset).toBe(0.0); + expect(ppeTexture.scale).toBe(0.06274509803921569); + + expect(ppeTexture.traits).toBeDefined(); + + const traits = ppeTexture.traits; + expect(traits.min).toBe(0); + expect(traits.max).toBe(16); + expect(traits.source).toBe("SIGZ"); + }); + + it("converts mesh primitive GPM extension data into structural metadata", async function () { + const extension = ngaGpmLocalExtension; + const gltf = createEmbeddedGltf(extension); + + const loader = new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + + await loader.load(); + await waitForLoaderProcess(loader, scene); + expect(() => loaderProcess(loader, scene)).not.toThrow(); + + const structuralMetadata = loader.structuralMetadata; + expect(structuralMetadata).toBeDefined(); + + const ppePropertyTexture = structuralMetadata.getPropertyTexture(0); + expect(ppePropertyTexture.id).toBe(0); + + const sigzProperty = ppePropertyTexture.getProperty("SIGZ"); + + expect(sigzProperty.textureReader.texture.width).toBe(16); + expect(sigzProperty.textureReader.texture.height).toBe(16); + }); + + it("destroys structural metadata", async function () { + const extension = ngaGpmLocalExtension; + const gltf = createEmbeddedGltf(extension); + + const destroyTexture = spyOn( + GltfTextureLoader.prototype, + "destroy" + ).and.callThrough(); + + const loader = new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + + await loader.load(); + + await waitForLoaderProcess(loader, scene); + expect(loader.structuralMetadata).toBeDefined(); + expect(loader.isDestroyed()).toBe(false); + + loader.destroy(); + + expect(loader.structuralMetadata).not.toBeDefined(); + expect(loader.isDestroyed()).toBe(true); + + expect(destroyTexture.calls.count()).toBe(1); + }); + + async function resolveAfterDestroy(rejectPromise) { + const extension = ngaGpmLocalExtension; + const gltf = createEmbeddedGltf(extension); + + const destroyTexture = spyOn( + GltfTextureLoader.prototype, + "destroy" + ).and.callThrough(); + + const loader = new GltfMeshPrimitiveGpmLoader({ + gltf: gltf, + extension: extension, + gltfResource: gltfResource, + baseResource: gltfResource, + supportedImageFormats: new SupportedImageFormats(), + frameState: mockFrameState, + }); + expect(loader.structuralMetadata).not.toBeDefined(); + const promise = loader.load(); + loader.destroy(); + + expect(loader.structuralMetadata).not.toBeDefined(); + expect(loader.isDestroyed()).toBe(true); + + expect(destroyTexture.calls.count()).toBe(1); + await expectAsync(promise).toBeResolved(); + } + + it("handles resolving resources after destroy", function () { + return resolveAfterDestroy(false); + }); + + it("handles rejecting resources after destroy", function () { + return resolveAfterDestroy(true); + }); + }, + "WebGL" +); From 616171f1fd26d38e9330e31daac0eb6f4765f04a Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 25 Sep 2024 15:18:03 +0200 Subject: [PATCH 19/38] Remove log. Check for definedness. --- packages/engine/Source/Scene/GltfLoader.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index c6733177bdd..ea4fa530352 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -2718,7 +2718,6 @@ function parse(loader, frameState) { // Load NGA_gpm_local from root object const gpmExtension = extensions.NGA_gpm_local; if (defined(gpmExtension)) { - console.log("Loading GPM from root"); const gltfGpmLocal = GltfGpmLoader.load(gpmExtension); loader._components.extensions["NGA_gpm_local"] = gltfGpmLocal; } @@ -2731,15 +2730,17 @@ function parse(loader, frameState) { if (defined(primitives)) { for (const primitive of primitives) { const primitiveExtensions = primitive.extensions; - const meshPrimitiveGpmExtension = primitiveExtensions.NGA_gpm_local; - if (defined(meshPrimitiveGpmExtension)) { - const promise = loadMeshPrimitiveGpm( - loader, - gltf, - meshPrimitiveGpmExtension, - frameState - ); - loader._loaderPromises.push(promise); + if (defined(primitiveExtensions)) { + const meshPrimitiveGpmExtension = primitiveExtensions.NGA_gpm_local; + if (defined(meshPrimitiveGpmExtension)) { + const promise = loadMeshPrimitiveGpm( + loader, + gltf, + meshPrimitiveGpmExtension, + frameState + ); + loader._loaderPromises.push(promise); + } } } } From 8c0f9263480991dded66326aa9be42c8ea1057bd Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 20:44:48 +0200 Subject: [PATCH 20/38] Remove experimental tag for getExtensions --- packages/engine/Source/Scene/Model/Model.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index 942cbf6a3d3..7e81d46aa9c 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -1784,8 +1784,6 @@ Model.prototype.applyArticulations = function () { * @exception {DeveloperError} The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true. * * @private - * @experimental This feature is subject to change without Cesium's standard deprecation policy. - * */ Model.prototype.getExtension = function (extensionName) { //>>includeStart('debug', pragmas.debug); From 5723c8b9499e904968e358dab2221cf019c2ab68 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 20:46:21 +0200 Subject: [PATCH 21/38] Add return type to JSDoc --- packages/engine/Source/Scene/Model/Model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index 7e81d46aa9c..4c79779b66c 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -1780,7 +1780,7 @@ Model.prototype.applyArticulations = function () { * this will return the model representation of the extension. * * @param {string} extensionName The name of the extension - * @returns The object, or `undefined` + * @returns {object|undefined} The object, or `undefined` * @exception {DeveloperError} The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true. * * @private From bb74ec4b4e3c7ff517eef97141d9f3fa90cef2ad Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 20:48:46 +0200 Subject: [PATCH 22/38] Add JSDoc for getExtension --- .../engine/Source/Scene/Model/Model3DTileContent.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js index 15b0e10f49e..bdc0b3831a8 100644 --- a/packages/engine/Source/Scene/Model/Model3DTileContent.js +++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js @@ -152,6 +152,19 @@ Object.defineProperties(Model3DTileContent.prototype, { }, }); +/** + * Returns the object that was created for the given extension. + * + * The given name may be the name of a glTF extension, like `"EXT_example_extension"`. + * If the specified extension was present in the root of the underlying glTF asset, + * and a loder for the specified extension has processed the extension data, then + * this will return the model representation of the extension. + * + * @param {string} extensionName The name of the extension + * @returns {object|undefined} The object, or `undefined` + * + * @private + */ Model3DTileContent.prototype.getExtension = function (extensionName) { const model = this._model; const extension = model.getExtension(extensionName); From 962a9864790a9bc224eb35fba9e3a9751c133fdc Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 20:50:00 +0200 Subject: [PATCH 23/38] Add type tag to property --- packages/engine/Source/Scene/ModelComponents.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/engine/Source/Scene/ModelComponents.js b/packages/engine/Source/Scene/ModelComponents.js index fa49d4111c2..fc72bf5baac 100644 --- a/packages/engine/Source/Scene/ModelComponents.js +++ b/packages/engine/Source/Scene/ModelComponents.js @@ -1145,6 +1145,9 @@ function Components() { /** * A mapping from extension names like `"EXT_example_extension"` to * the object that was created from the extension input + * + * @type {object} + * @private */ this.extensions = {}; } From 804fa0a68bfb38298b951fdba7549c13a2a29f95 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 20:57:31 +0200 Subject: [PATCH 24/38] More elaborate warning text --- packages/engine/Source/Scene/GltfLoader.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 739f129d0f9..d853d90d73d 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -482,7 +482,10 @@ function processLoaders(loader, frameState) { if (defined(loader._components.structuralMetadata)) { oneTimeWarning( "structural-metadata-gpm", - "Structural metadata is replaced with GPM data", + "The model defines both the 'EXT_structural_metadata' extension and the " + + "'NGA_gpm_local' extension. The data from the 'EXT_structural_metadata' " + + "extension will be replaced with the data from the 'NGA_gpm_local' extension, " + + "and will no longer be available for styling and picking.", ); } loader._components.structuralMetadata = From 905925a3df64452622a5cd54674fd4b46f6ff38c Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 21:07:54 +0200 Subject: [PATCH 25/38] Removed obsolete part of JSDoc --- .../engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index b872a53c7c5..ab9322002a4 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -11,8 +11,6 @@ import StorageType from "./StorageType.js"; /** * Loads glTF NGA_gpm_local from the root of a glTF object - *

- * Implementation note: This is an experimental implementation. * * @alias GltfGpmLoader * @constructor From e887e538b87174b5bca84ace878bce6c11200136 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 21:14:25 +0200 Subject: [PATCH 26/38] Add JSDoc return type info --- .../Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index ab9322002a4..4d5a95cc965 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -30,7 +30,7 @@ function GltfGpmLoader() {} * column-major order. * * @param {number[]} array The input array - * @returns The Matrix3 + * @returns {Matrix3} The Matrix3 */ function createCovarianceMatrixFromUpperTriangle(array) { const covarianceMatrix = new Matrix3( @@ -51,7 +51,7 @@ function createCovarianceMatrixFromUpperTriangle(array) { * Creates an `AnchorPointDirect` from the given JSON representation * * @param {object} anchorPointDirectJson The input JSON - * @returns The `AnchorPointDirect` + * @returns {AnchorPointDirect} The direct anchor point */ function createAnchorPointDirect(anchorPointDirectJson) { const position = Cartesian3.fromArray( @@ -75,7 +75,7 @@ function createAnchorPointDirect(anchorPointDirectJson) { * Creates an `AnchorPointIndirect` from the given JSON representation * * @param {object} anchorPointIndirectJson The input JSON - * @returns The `AnchorPointIndirect` + * @returns {AnchorPointIndirect} The indirect anchor point */ function createAnchorPointIndirect(anchorPointIndirectJson) { const position = Cartesian3.fromArray( @@ -103,7 +103,7 @@ function createAnchorPointIndirect(anchorPointIndirectJson) { * Creates a `CorrelationGroup` from the given JSON representation * * @param {object} correlationGroupJson The input JSON - * @returns The `CorrelationGroup` + * @returns {CorrelationGroup} The correlation group */ function createCorrelationGroup(correlationGroupJson) { const groupFlags = correlationGroupJson.groupFlags; From 13b2db09dc56712fac91bd0d73b96f8d2538c6af Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 21:16:24 +0200 Subject: [PATCH 27/38] Change error type for load function --- .../Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index 4d5a95cc965..25143d00ba2 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -1,7 +1,7 @@ import Cartesian3 from "../../../../Core/Cartesian3.js"; import Check from "../../../../Core/Check.js"; -import DeveloperError from "../../../../Core/DeveloperError.js"; import Matrix3 from "../../../../Core/Matrix3.js"; +import RuntimeError from "../../../../Core/RuntimeError.js"; import AnchorPointDirect from "./AnchorPointDirect.js"; import AnchorPointIndirect from "./AnchorPointIndirect.js"; import CorrelationGroup from "./CorrelationGroup.js"; @@ -136,9 +136,8 @@ function createCorrelationGroup(correlationGroupJson) { * * @param {object} gltfGpmLocalJson The extension object * @returns {GltfGpmLocal} The parsed object + * @throws RuntimeError When the given object contains invalid storage types. * @private - * @throws DeveloperError When the given object is `undefined` - * or contains invalid structures */ GltfGpmLoader.load = function (gltfGpmLocalJson) { //>>includeStart('debug', pragmas.debug); @@ -152,7 +151,7 @@ GltfGpmLoader.load = function (gltfGpmLocalJson) { if (storageType === StorageType.Indirect) { return GltfGpmLoader.loadIndirect(gltfGpmLocalJson); } - throw new DeveloperError( + throw new RuntimeError( `Invalid storage type in NGA_gpm_local - expected 'Direct' or 'Indirect', but found ${storageType}`, ); }; From ac78182077e17745236cd61b86ba8522c32f436b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 21:29:29 +0200 Subject: [PATCH 28/38] List supported extension in model documentation --- packages/engine/Source/Scene/Model/Model.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index 4c79779b66c..0892388a51a 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -105,6 +105,9 @@ import pickModel from "./pickModel.js"; *

  • * {@link https://github.com/KhronosGroup/glTF/blob/main/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes} *
  • + *
  • + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} + *
  • * *

    *

    From e61667c10f67ace80c730ab035f10d805be0163d Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 27 Sep 2024 21:32:06 +0200 Subject: [PATCH 29/38] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index bf6d8f4a47e..3bf60ce39bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ - Added `enableVerticalExaggeration` option to models. Set this value to `false` to prevent model exaggeration when `Scene.verticalExaggeration` is set to a value other than `1.0`. [#12141](https://github.com/CesiumGS/cesium/pull/12141) - Added `CallbackPositionProperty` to allow lazy entity position evaluation. [#12170](https://github.com/CesiumGS/cesium/pull/12170) +- Added experimental support for the `NGA_gpm_local` glTF extension, for GPM 1.2 [#12204](https://github.com/CesiumGS/cesium/pull/12204) ##### Fixes :wrench: From e94ca56b6791b5783423392d99ea6c664d10d901 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 13:19:40 +0200 Subject: [PATCH 30/38] Try to clarify private and experimental --- .../Model/Extensions/Gpm/AnchorPointDirect.js | 3 --- .../Model/Extensions/Gpm/AnchorPointIndirect.js | 4 ---- .../Scene/Model/Extensions/Gpm/CorrelationGroup.js | 4 ---- .../Scene/Model/Extensions/Gpm/GltfGpmLoader.js | 1 - .../Scene/Model/Extensions/Gpm/GltfGpmLocal.js | 14 ++++---------- .../Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js | 1 - .../Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js | 2 -- .../Scene/Model/Extensions/Gpm/PpeMetadata.js | 4 ---- .../Scene/Model/Extensions/Gpm/PpeTexture.js | 7 ------- .../Source/Scene/Model/Extensions/Gpm/Spdcf.js | 5 ----- packages/engine/Source/Scene/Model/Model.js | 4 ++-- 11 files changed, 6 insertions(+), 43 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js index 903330dada7..05bc225f926 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js @@ -3,7 +3,6 @@ import Check from "../../../../Core/Check.js"; /** * Metadata for one stored anchor point using direct storage * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function AnchorPointDirect(options) { @@ -23,7 +22,6 @@ Object.defineProperties(AnchorPointDirect.prototype, { * @memberof AnchorPointDirect.prototype * @type {Cartesian3} * @readonly - * @private */ position: { get: function () { @@ -38,7 +36,6 @@ Object.defineProperties(AnchorPointDirect.prototype, { * @memberof AnchorPointDirect.prototype * @type {Cartesian3} * @readonly - * @private */ adjustmentParams: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js index f0102db3294..03599b48ce8 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js @@ -3,7 +3,6 @@ import Check from "../../../../Core/Check.js"; /** * Metadata for one stored anchor point. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function AnchorPointIndirect(options) { @@ -25,7 +24,6 @@ Object.defineProperties(AnchorPointIndirect.prototype, { * @memberof AnchorPointIndirect.prototype * @type {Cartesian3} * @readonly - * @private */ position: { get: function () { @@ -40,7 +38,6 @@ Object.defineProperties(AnchorPointIndirect.prototype, { * @memberof AnchorPointIndirect.prototype * @type {Cartesian3} * @readonly - * @private */ adjustmentParams: { get: function () { @@ -54,7 +51,6 @@ Object.defineProperties(AnchorPointIndirect.prototype, { * @memberof AnchorPointIndirect.prototype * @type {Matrix3} * @readonly - * @private */ covarianceMatrix: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js index cc82812aa60..23812c03871 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js @@ -4,7 +4,6 @@ import Check from "../../../../Core/Check.js"; * Metadata identifying parameters using same correlation modeling and * associated correlation parameters. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function CorrelationGroup(options) { @@ -27,7 +26,6 @@ Object.defineProperties(CorrelationGroup.prototype, { * @memberof CorrelationGroup.prototype * @type {boolean[]} * @readonly - * @private */ groupFlags: { get: function () { @@ -41,7 +39,6 @@ Object.defineProperties(CorrelationGroup.prototype, { * @memberof CorrelationGroup.prototype * @type {Cartesian3} * @readonly - * @private */ rotationThetas: { get: function () { @@ -55,7 +52,6 @@ Object.defineProperties(CorrelationGroup.prototype, { * @memberof CorrelationGroup.prototype * @type {Spdcf[]} * @readonly - * @private */ params: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js index 25143d00ba2..f3cd3e2dd2d 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLoader.js @@ -20,7 +20,6 @@ import StorageType from "./StorageType.js"; * @param {string} [options.extension] The NGA_gpm_local extension object. * * @private - * @experimental This feature is subject to change without Cesium's standard deprecation policy. */ function GltfGpmLoader() {} diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js index 76878a9c4c7..73fc5789de1 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -7,7 +7,6 @@ import StorageType from "./StorageType.js"; * The GPM metadata for a Ground-Space Indirect implementation stored * locally (i.e. a tile and/or leaf node). * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function GltfGpmLocal(options) { @@ -76,7 +75,6 @@ Object.defineProperties(GltfGpmLocal.prototype, { * @memberof GltfGpmLocal.prototype * @type {StorageType} * @readonly - * @private */ storageType: { get: function () { @@ -88,9 +86,8 @@ Object.defineProperties(GltfGpmLocal.prototype, { * Array of stored indirect anchor points * * @memberof GltfGpmLocal.prototype - * @type {AnchorPointIndirect[]} + * @type {AnchorPointIndirect[]|undefined} * @readonly - * @private */ anchorPointsIndirect: { get: function () { @@ -102,9 +99,8 @@ Object.defineProperties(GltfGpmLocal.prototype, { * Array of stored direct anchor points * * @memberof GltfGpmLocal.prototype - * @type {AnchorPointDirect[]} + * @type {AnchorPointDirect[]|undefined} * @readonly - * @private */ anchorPointsDirect: { get: function () { @@ -117,9 +113,8 @@ Object.defineProperties(GltfGpmLocal.prototype, { * associated correlation parameters * * @memberof GltfGpmLocal.prototype - * @type {CorrelationGroup[]} + * @type {CorrelationGroup[]|undefined} * @readonly - * @private */ intraTileCorrelationGroups: { get: function () { @@ -131,9 +126,8 @@ Object.defineProperties(GltfGpmLocal.prototype, { * The full covariance of anchor point parameters * * @memberof GltfGpmLocal.prototype - * @type {Matrix3} + * @type {Matrix3|undefined} * @readonly - * @private */ covarianceDirect: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index 76c860fe7ce..b718ff49e50 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -37,7 +37,6 @@ import MeshPrimitiveGpmLocal from "./MeshPrimitiveGpmLocal.js"; * @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. * * @private - * @experimental This feature is subject to change without Cesium's standard deprecation policy. */ function GltfMeshPrimitiveGpmLoader(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js index 1ca12213774..5cdf62f3794 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js @@ -1,7 +1,6 @@ /** * Local Generic Point-cloud Model information about a glTF primitive. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function MeshPrimitiveGpmLocal(options) { @@ -15,7 +14,6 @@ Object.defineProperties(MeshPrimitiveGpmLocal.prototype, { * @memberof MeshPrimitiveGpmLocal.prototype * @type {PpeTexture[]|undefined} * @readonly - * @private */ ppeTextures: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index 66d38ef40cb..457779cf4b4 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -3,7 +3,6 @@ import Check from "../../../../Core/Check.js"; /** * Metadata related to the stored PPE data. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function PpeMetadata(options) { @@ -25,7 +24,6 @@ Object.defineProperties(PpeMetadata.prototype, { * @memberof PpeMetadata.prototype * @type {number|undefined} * @readonly - * @private */ min: { get: function () { @@ -41,7 +39,6 @@ Object.defineProperties(PpeMetadata.prototype, { * @memberof PpeMetadata.prototype * @type {number|undefined} * @readonly - * @private */ max: { get: function () { @@ -55,7 +52,6 @@ Object.defineProperties(PpeMetadata.prototype, { * @memberof PpeMetadata.prototype * @type {PpeSource} * @readonly - * @private */ source: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 8b352956758..f8b8109f773 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -7,7 +7,6 @@ import Check from "../../../../Core/Check.js"; * and an optional `texCoord)`, with additional properties that * describe the structure of the metdata that is stored in the texture. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function PpeTexture(options) { @@ -32,7 +31,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {PpeMetadata} * @readonly - * @private */ traits: { get: function () { @@ -47,7 +45,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {number|undefined} * @readonly - * @private */ noData: { get: function () { @@ -61,7 +58,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {number|undefined} * @readonly - * @private */ offset: { get: function () { @@ -75,7 +71,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {number|undefined} * @readonly - * @private */ scale: { get: function () { @@ -89,7 +84,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {number} * @readonly - * @private */ index: { get: function () { @@ -103,7 +97,6 @@ Object.defineProperties(PpeTexture.prototype, { * @memberof PpeTexture.prototype * @type {number|undefined} * @readonly - * @private */ texCoord: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js index 7687ed32ed8..237b34f6ac4 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js @@ -6,7 +6,6 @@ import Check from "../../../../Core/Check.js"; * Parameters (A, alpha, beta, T) used to describe the correlation decrease * between points as a function of delta time. * - * @private * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function Spdcf(options) { @@ -33,7 +32,6 @@ Object.defineProperties(Spdcf.prototype, { * @memberof Spdcf.prototype * @type {number} * @readonly - * @private */ A: { get: function () { @@ -47,7 +45,6 @@ Object.defineProperties(Spdcf.prototype, { * @memberof Spdcf.prototype * @type {number} * @readonly - * @private */ alpha: { get: function () { @@ -61,7 +58,6 @@ Object.defineProperties(Spdcf.prototype, { * @memberof Spdcf.prototype * @type {number} * @readonly - * @private */ beta: { get: function () { @@ -75,7 +71,6 @@ Object.defineProperties(Spdcf.prototype, { * @memberof Spdcf.prototype * @type {number} * @readonly - * @private */ T: { get: function () { diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js index 0892388a51a..15474e5df2e 100644 --- a/packages/engine/Source/Scene/Model/Model.js +++ b/packages/engine/Source/Scene/Model/Model.js @@ -106,7 +106,7 @@ import pickModel from "./pickModel.js"; * {@link https://github.com/KhronosGroup/glTF/blob/main/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes} * *

  • - * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local (experimental)} *
  • * *

    @@ -1786,7 +1786,7 @@ Model.prototype.applyArticulations = function () { * @returns {object|undefined} The object, or `undefined` * @exception {DeveloperError} The model is not loaded. Use Model.readyEvent or wait for Model.ready to be true. * - * @private + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ Model.prototype.getExtension = function (extensionName) { //>>includeStart('debug', pragmas.debug); From 72414c50ffc0ce737e96d61a763460ffbd83547a Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 13:52:13 +0200 Subject: [PATCH 31/38] Add links to GPM spec. Updated comments. --- .../Model/Extensions/Gpm/AnchorPointDirect.js | 5 ++++- .../Extensions/Gpm/AnchorPointIndirect.js | 3 +++ .../Model/Extensions/Gpm/CorrelationGroup.js | 3 +++ .../Model/Extensions/Gpm/GltfGpmLocal.js | 20 +++++++++++++++++++ .../Gpm/GltfMeshPrimitiveGpmLoader.js | 4 ++-- .../Scene/Model/Extensions/Gpm/PpeMetadata.js | 5 ++++- .../Scene/Model/Extensions/Gpm/PpeSource.js | 5 ++++- .../Scene/Model/Extensions/Gpm/PpeTexture.js | 3 +++ .../Scene/Model/Extensions/Gpm/Spdcf.js | 12 +++++++++-- .../Scene/Model/Extensions/Gpm/StorageType.js | 5 ++++- 10 files changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js index 05bc225f926..430240fee98 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js @@ -1,7 +1,10 @@ import Check from "../../../../Core/Check.js"; /** - * Metadata for one stored anchor point using direct storage + * Metadata for one stored anchor point using direct storage. + * + * This reflects the `anchronPointDirect` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js index 03599b48ce8..7abe2db9404 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js @@ -3,6 +3,9 @@ import Check from "../../../../Core/Check.js"; /** * Metadata for one stored anchor point. * + * This reflects the `anchronPointIndirect` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function AnchorPointIndirect(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js index 23812c03871..abf0a4e1262 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js @@ -4,6 +4,9 @@ import Check from "../../../../Core/Check.js"; * Metadata identifying parameters using same correlation modeling and * associated correlation parameters. * + * This reflects the `correlationGroup` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function CorrelationGroup(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js index 73fc5789de1..84ae342bf56 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -7,6 +7,26 @@ import StorageType from "./StorageType.js"; * The GPM metadata for a Ground-Space Indirect implementation stored * locally (i.e. a tile and/or leaf node). * + * This reflects the root extension object of the {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} + * glTF extension. When a model that contains this extension was loaded, + * then an object of this type can be obtained by calling + * ``` + * const gltfGpmLocal = model.getExtension("NGA_gpm_local"); + * ``` + * + * The storage type determines the presence of the optional properties: + *
      + *
    • + * When the storage type is `StorageType.Indirect`, then the + * `anchorPointsIndirect` and `intraTileCorrelationGroups` + * are present. + *
    • + *
    • + * When the storage type is `StorageType.Direct`, then the + * `anchorPointsDirect` and `covarianceDirect` are present. + *
    • + *
    + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function GltfGpmLocal(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index b718ff49e50..e1667266975 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -232,7 +232,7 @@ GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); /** * Create the JSON description of a metadata class that treats - * the given PPE texture as a property texture property(!). + * the given PPE texture as a property texture property. * * @param {any} ppeTexture - The PPE texture * @param {number} index - The index of the texture in the extension @@ -346,7 +346,7 @@ GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( * * Each glTF may define multiple `ppeTexture` objects within the * `NGA_gpm_local` extensions. Each of these textures corresponds - * to one 'property texture property(!)' in a metadata schema. + * to one 'property texture property' in a metadata schema. * * This method will create an array where each element is a (JSON) * string representation of the parts of a GPM PPE texture definition diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index 457779cf4b4..46949992a3d 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -1,7 +1,10 @@ import Check from "../../../../Core/Check.js"; /** - * Metadata related to the stored PPE data. + * Metadata related to the stored PPE (Per-Point Error) data. + * + * This reflects the `ppeMetadata` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js index d44acabe4e1..db0f3676f27 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js @@ -1,5 +1,8 @@ /** - * An enum of per-point error sources + * An enum of per-point error sources. + * + * This reflects the `ppeMetadata.source` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * * @enum {string} * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index f8b8109f773..216764da299 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -3,6 +3,9 @@ import Check from "../../../../Core/Check.js"; /** * PPE (Per-Point Error) texture in `NGA_gpm_local`. * + * This reflects the `ppeTexture` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. + * * This is a valid glTF `TextureInfo` object (with a required `index` * and an optional `texCoord)`, with additional properties that * describe the structure of the metdata that is stored in the texture. diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js index 237b34f6ac4..74ae2c607c8 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js @@ -3,8 +3,16 @@ import Check from "../../../../Core/Check.js"; /** * Variables for a Strictly Positive-Definite Correlation Function. * - * Parameters (A, alpha, beta, T) used to describe the correlation decrease - * between points as a function of delta time. + * This reflects the `spdcf` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. + * Instances of this type are stored as the parameters within a + * `CorrelationGroup`. + * + * Parameters (A, alpha, beta, T) describe the correlation decrease + * between points as a function of delta time: + * ``` + * spdcf(delta_t) = A_t * (alpha_t + ((1 - alpha_t)(1 + beta_t)) / (beta_t + e^(delta_t/T_t))) + * ``` * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js index f706c32b63e..94a864b6be3 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/StorageType.js @@ -1,5 +1,8 @@ /** - * An enum of storage types for covariance information + * An enum of storage types for covariance information. + * + * This reflects the `gltfGpmLocal.storageType` definition of the + * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * * @enum {string} * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. From 55df6c9b8fe2d145f66061bd0546e3b9931a914b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 14:29:38 +0200 Subject: [PATCH 32/38] Document constructor options --- .../Model/Extensions/Gpm/GltfGpmLocal.js | 19 +++++++++++++++++++ .../Scene/Model/Extensions/Gpm/PpeMetadata.js | 12 ++++++++++++ .../Scene/Model/Extensions/Gpm/PpeTexture.js | 15 +++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js index 84ae342bf56..f58aa27fd36 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -3,6 +3,23 @@ import Check from "../../../../Core/Check.js"; import RuntimeError from "../../../../Core/RuntimeError.js"; import StorageType from "./StorageType.js"; +/** + * @typedef {object} GltfGpmLocal.ConstructorOptions + * + * Initialization options for the GltfGpmLocal constructor + * + * @property {string} storageType The storage type. + * This must be one of the `StorageType` constants, i.e. `Direct` or `Indirect`. + * @property {AnchorPointIndirect[]|undefined} [anchorPointsIndirect] The indirect anchor points. + * This must be present if and only if the storage type is `Indirect`. + * @property {CorrelationGroup[]|undefined} [intraTileCorrelationGroups] The intra-tile correlation groups. + * This must be present if and only if the storage type is `Indirect`. + * @property {AnchorPointDirect[]|undefined} [anchorPointsDirect] The direct anchor points. + * This must be present if and only if the storage type is `Direct`. + * @property {Matrix3|undefined} [covarianceDirect] The covariance of anchor point parameters. + * This must be present if and only if the storage type is `Direct`. + */ + /** * The GPM metadata for a Ground-Space Indirect implementation stored * locally (i.e. a tile and/or leaf node). @@ -27,6 +44,8 @@ import StorageType from "./StorageType.js"; * * * + * @param {GltfGpmLocal.ConstructorOptions} options An object describing initialization options + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function GltfGpmLocal(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index 46949992a3d..88bcb70ebee 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -1,11 +1,23 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} PpeMetadata.ConstructorOptions + * + * Initialization options for the PpeMetadata constructor + * + * @property {PpeSource} source The source of the error data + * @property {number|undefined} [min] Minimum allowed value for the property. + * @property {number|undefined} [max] Maximum allowed value for the property. + */ + /** * Metadata related to the stored PPE (Per-Point Error) data. * * This reflects the `ppeMetadata` definition of the * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * + * @param {PpeMetadata.ConstructorOptions} options An object describing initialization options + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function PpeMetadata(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 216764da299..7588fd2a4ed 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -1,5 +1,18 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} PpeTexture.ConstructorOptions + * + * Initialization options for the PpeTexture constructor + * + * @property {PpeMetadata} traits The traits that indicate which data is stored in this texture + * @property {number} index The index of the texture inside the glTF textures array + * @property {number|undefined} [texCoord] The optional set index for the TEXCOORD attribute + * @property {number|undefined} [noData] The value to represent missing data + * @property {number|undefined} [offset] An offset to apply to property values. + * @property {number|undefined} [scale] A scale to apply to property values. + */ + /** * PPE (Per-Point Error) texture in `NGA_gpm_local`. * @@ -10,6 +23,8 @@ import Check from "../../../../Core/Check.js"; * and an optional `texCoord)`, with additional properties that * describe the structure of the metdata that is stored in the texture. * + * @param {PpeTexture.ConstructorOptions} options An object describing initialization options + * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function PpeTexture(options) { From b8b5445903bb9fb9b3a9d4c3a55f58dfa38bb47c Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 14:30:02 +0200 Subject: [PATCH 33/38] Avoid unnecessary constructor options --- .../Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js index 5cdf62f3794..c8b76f25b0d 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js @@ -1,10 +1,12 @@ /** * Local Generic Point-cloud Model information about a glTF primitive. * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * @param {PpeTexture[]} ppeTextures The Per-Point Error textures + * + * @private */ -function MeshPrimitiveGpmLocal(options) { - this._ppeTextures = options.ppeTextures; +function MeshPrimitiveGpmLocal(ppeTextures) { + this._ppeTextures = ppeTextures; } Object.defineProperties(MeshPrimitiveGpmLocal.prototype, { From e4efa515554dcb2b203bf7bd00e9114711218ab2 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 14:30:23 +0200 Subject: [PATCH 34/38] Add underscore to internal functions --- .../Gpm/GltfMeshPrimitiveGpmLoader.js | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index e1667266975..7be02214431 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -131,9 +131,9 @@ Object.defineProperties(GltfMeshPrimitiveGpmLoader.prototype, { }, }); -GltfMeshPrimitiveGpmLoader.prototype.loadResources = async function () { +GltfMeshPrimitiveGpmLoader.prototype._loadResources = async function () { try { - const texturesPromise = this.loadTextures(); + const texturesPromise = this._loadTextures(); await texturesPromise; if (this.isDestroyed()) { @@ -167,7 +167,7 @@ GltfMeshPrimitiveGpmLoader.prototype.load = function () { } this._state = ResourceLoaderState.LOADING; - this._promise = this.loadResources(this); + this._promise = this._loadResources(this); return this._promise; }; @@ -185,7 +185,7 @@ function gatherUsedTextureIds(gpmExtension) { return textureIds; } -GltfMeshPrimitiveGpmLoader.prototype.loadTextures = function () { +GltfMeshPrimitiveGpmLoader.prototype._loadTextures = function () { let textureIds; if (defined(this._extension)) { textureIds = gatherUsedTextureIds(this._extension); @@ -224,7 +224,7 @@ GltfMeshPrimitiveGpmLoader.prototype.loadTextures = function () { * A static mapping from PPE texture property identifier keys * to `MetadataSchema` instances. This is used to create each * schema (with a certain structure) only ONCE in - * obtainPpeTexturesMetadataSchema + * _obtainPpeTexturesMetadataSchema * * @private */ @@ -238,7 +238,7 @@ GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); * @param {number} index - The index of the texture in the extension * @returns The class JSON */ -GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson = function ( +GltfMeshPrimitiveGpmLoader._createPpeTextureClassJson = function ( ppeTexture, index, ) { @@ -295,16 +295,16 @@ GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson = function ( * schema that reflects the structure of the PPE textures in the * given instance, creating and caching it if necessary. * - * For details on the cache key, see `collectPpeTexturePropertyIdentifiers` + * For details on the cache key, see `_collectPpeTexturePropertyIdentifiers` * * @param {MeshPrimitiveGpmLocal} meshPrimitiveGpmLocal The extension object * @returns The `MetadataSchema` */ -GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( +GltfMeshPrimitiveGpmLoader._obtainPpeTexturesMetadataSchema = function ( meshPrimitiveGpmLocal, ) { const ppeTexturePropertyIdentifiers = - GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers( + GltfMeshPrimitiveGpmLoader._collectPpeTexturePropertyIdentifiers( meshPrimitiveGpmLocal, ); const key = ppeTexturePropertyIdentifiers.toString(); @@ -324,7 +324,7 @@ GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( for (let i = 0; i < ppeTextures.length; i++) { const ppeTexture = ppeTextures[i]; const classId = `ppeTexture_${i}`; - const classJson = GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson( + const classJson = GltfMeshPrimitiveGpmLoader._createPpeTextureClassJson( ppeTexture, i, ); @@ -356,7 +356,7 @@ GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema = function ( * @param {MeshPrimitiveGpmLocal} meshPrimitiveGpmLocal The extension object * @returns The identifiers */ -GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers = function ( +GltfMeshPrimitiveGpmLoader._collectPpeTexturePropertyIdentifiers = function ( meshPrimitiveGpmLocal, ) { const ppeTexturePropertyIdentifiers = []; @@ -367,7 +367,7 @@ GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers = function ( // to define two PPE textures as "representing the same // property texture property" within a structural metadata // schema. - const classJson = GltfMeshPrimitiveGpmLoader.createPpeTextureClassJson( + const classJson = GltfMeshPrimitiveGpmLoader._createPpeTextureClassJson( ppeTexture, i, ); @@ -389,13 +389,13 @@ GltfMeshPrimitiveGpmLoader.collectPpeTexturePropertyIdentifiers = function ( * @param {object} textures The mapping from texture ID to texture objects * @returns The `StructuralMetadata` object */ -GltfMeshPrimitiveGpmLoader.convertToStructuralMetadata = function ( +GltfMeshPrimitiveGpmLoader._convertToStructuralMetadata = function ( meshPrimitiveGpmLocal, textures, ) { const propertyTextures = []; const ppeTexturesMetadataSchema = - GltfMeshPrimitiveGpmLoader.obtainPpeTexturesMetadataSchema( + GltfMeshPrimitiveGpmLoader._obtainPpeTexturesMetadataSchema( meshPrimitiveGpmLocal, ); const ppeTextures = meshPrimitiveGpmLocal.ppeTextures; @@ -510,13 +510,11 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { ppeTextures.push(ppeTexture); } } - const meshPrimitiveGpmLocal = new MeshPrimitiveGpmLocal({ - ppeTextures: ppeTextures, - }); + const meshPrimitiveGpmLocal = new MeshPrimitiveGpmLocal(ppeTextures); this._meshPrimitiveGpmLocal = meshPrimitiveGpmLocal; const structuralMetadata = - GltfMeshPrimitiveGpmLoader.convertToStructuralMetadata( + GltfMeshPrimitiveGpmLoader._convertToStructuralMetadata( meshPrimitiveGpmLocal, textures, ); @@ -526,7 +524,7 @@ GltfMeshPrimitiveGpmLoader.prototype.process = function (frameState) { return true; }; -GltfMeshPrimitiveGpmLoader.prototype.unloadTextures = function () { +GltfMeshPrimitiveGpmLoader.prototype._unloadTextures = function () { const textureLoaders = this._textureLoaders; const textureLoadersLength = textureLoaders.length; for (let i = 0; i < textureLoadersLength; ++i) { @@ -541,7 +539,7 @@ GltfMeshPrimitiveGpmLoader.prototype.unloadTextures = function () { * @private */ GltfMeshPrimitiveGpmLoader.prototype.unload = function () { - this.unloadTextures(); + this._unloadTextures(); this._gltf = undefined; this._extension = undefined; this._structuralMetadata = undefined; From 860b05b25271607755173654600974b3ee9065f6 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 14:39:53 +0200 Subject: [PATCH 35/38] Further clarification for private --- .../engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js | 2 +- packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js | 2 +- packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index 88bcb70ebee..e5289c2ef94 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -18,7 +18,7 @@ import Check from "../../../../Core/Check.js"; * * @param {PpeMetadata.ConstructorOptions} options An object describing initialization options * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * @private */ function PpeMetadata(options) { //>>includeStart('debug', pragmas.debug); diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js index db0f3676f27..92d1bbedd81 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeSource.js @@ -5,7 +5,7 @@ * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * * @enum {string} - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * @private */ const PpeSource = { /** diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 7588fd2a4ed..24fac262afd 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -25,7 +25,7 @@ import Check from "../../../../Core/Check.js"; * * @param {PpeTexture.ConstructorOptions} options An object describing initialization options * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * @private */ function PpeTexture(options) { //>>includeStart('debug', pragmas.debug); From 9bfe61afc1827a6ad9032138f5ffdcc8cbe77d38 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 15:13:16 +0200 Subject: [PATCH 36/38] Update spec for changed error type --- .../Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js index e6f35b16e09..ddaf8ddfb39 100644 --- a/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js +++ b/packages/engine/Specs/Scene/Model/Extensions/Gpm/GltfGpmLoaderSpec.js @@ -12,7 +12,7 @@ describe("Scene/Model/Extensions/Gpm/GltfGpmLoader", function () { }; expect(function () { GltfGpmLoader.load(gltfGpmLocalJson); - }).toThrowDeveloperError(); + }).toThrowError(); }); it("load throws for storageType Direct without anchorPointsDirect", async function () { @@ -21,7 +21,7 @@ describe("Scene/Model/Extensions/Gpm/GltfGpmLoader", function () { }; expect(function () { GltfGpmLoader.load(gltfGpmLocalJson); - }).toThrowDeveloperError(); + }).toThrowError(); }); it("load throws for storageType Direct without covarianceDirectUpperTriangle", async function () { @@ -36,7 +36,7 @@ describe("Scene/Model/Extensions/Gpm/GltfGpmLoader", function () { }; expect(function () { GltfGpmLoader.load(gltfGpmLocalJson); - }).toThrowDeveloperError(); + }).toThrowError(); }); it("load returns result for valid JSON for storageType Direct", async function () { From 9805652f03e8ad24895a78929713e1bfd8b8710b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 28 Sep 2024 15:13:37 +0200 Subject: [PATCH 37/38] Further options- and constructor documentation --- .../Model/Extensions/Gpm/AnchorPointDirect.js | 11 +++++++++++ .../Model/Extensions/Gpm/AnchorPointIndirect.js | 12 ++++++++++++ .../Model/Extensions/Gpm/CorrelationGroup.js | 15 +++++++++++++++ .../Scene/Model/Extensions/Gpm/GltfGpmLocal.js | 1 + .../Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js | 1 + .../Scene/Model/Extensions/Gpm/PpeMetadata.js | 1 + .../Scene/Model/Extensions/Gpm/PpeTexture.js | 1 + .../Source/Scene/Model/Extensions/Gpm/Spdcf.js | 13 +++++++++++++ 8 files changed, 55 insertions(+) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js index 430240fee98..7f20273f7a9 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointDirect.js @@ -1,11 +1,22 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} AnchorPointDirect.ConstructorOptions + * + * Initialization options for the AnchorPointDirect constructor + * + * @property {Cartesian3} position Anchor point geographic coordinates + * @property {Cartesian3} adjustmentParams The adjustment values in meters + */ + /** * Metadata for one stored anchor point using direct storage. * * This reflects the `anchronPointDirect` definition of the * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * + * @constructor + * @param {AnchorPointDirect.ConstructorOptions} options An object describing initialization options * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function AnchorPointDirect(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js index 7abe2db9404..b7f368b6d85 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/AnchorPointIndirect.js @@ -1,11 +1,23 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} AnchorPointIndirect.ConstructorOptions + * + * Initialization options for the AnchorPointIndirect constructor + * + * @property {Cartesian3} position Anchor point geographic coordinates + * @property {Cartesian3} adjustmentParams The adjustment values in meters + * @property {Matrix3} covarianceMatrix The 3x3 covariance matrix + */ + /** * Metadata for one stored anchor point. * * This reflects the `anchronPointIndirect` definition of the * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * + * @constructor + * @param {AnchorPointIndirect.ConstructorOptions} options An object describing initialization options * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function AnchorPointIndirect(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js index abf0a4e1262..919ef9ed999 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/CorrelationGroup.js @@ -1,5 +1,18 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} CorrelationGroup.ConstructorOptions + * + * Initialization options for the CorrelationGroup constructor + * + * @property {boolean[]} groupFlags Array of 3 booleans indicating if + * parameters delta-x delta-y delta-z used in the correlation group + * @property {Cartesian3} rotationThetas Rotations in milliradians + * about X, Y, Z axes, respectively + * @property {Spdcf[]} params Array of `Spdcf` (Strictly Positive-Definite + * Correlation Function) parameters, for the U, V, W directions, respectively + */ + /** * Metadata identifying parameters using same correlation modeling and * associated correlation parameters. @@ -7,6 +20,8 @@ import Check from "../../../../Core/Check.js"; * This reflects the `correlationGroup` definition of the * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * + * @constructor + * @param {CorrelationGroup.ConstructorOptions} options An object describing initialization options * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function CorrelationGroup(options) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js index f58aa27fd36..80339c2247e 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfGpmLocal.js @@ -44,6 +44,7 @@ import StorageType from "./StorageType.js"; * * * + * @constructor * @param {GltfGpmLocal.ConstructorOptions} options An object describing initialization options * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js index c8b76f25b0d..aab7130e859 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/MeshPrimitiveGpmLocal.js @@ -3,6 +3,7 @@ * * @param {PpeTexture[]} ppeTextures The Per-Point Error textures * + * @constructor * @private */ function MeshPrimitiveGpmLocal(ppeTextures) { diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js index e5289c2ef94..d7c077fe603 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeMetadata.js @@ -16,6 +16,7 @@ import Check from "../../../../Core/Check.js"; * This reflects the `ppeMetadata` definition of the * {@link https://nsgreg.nga.mil/csmwg.jsp|NGA_gpm_local} glTF extension. * + * @constructor * @param {PpeMetadata.ConstructorOptions} options An object describing initialization options * * @private diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js index 24fac262afd..e81d7605448 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/PpeTexture.js @@ -23,6 +23,7 @@ import Check from "../../../../Core/Check.js"; * and an optional `texCoord)`, with additional properties that * describe the structure of the metdata that is stored in the texture. * + * @constructor * @param {PpeTexture.ConstructorOptions} options An object describing initialization options * * @private diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js index 74ae2c607c8..1cc01a96b53 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/Spdcf.js @@ -1,5 +1,16 @@ import Check from "../../../../Core/Check.js"; +/** + * @typedef {object} Spdcf.ConstructorOptions + * + * Initialization options for the Spdcf constructor + * + * @property {number} A The factor A, in (0, 1] + * @property {number} alpha The alpha value, in [0, 1) + * @property {number} beta The beta value, in [0, 10] + * @property {number} T the tau value, in (0, +inf) + */ + /** * Variables for a Strictly Positive-Definite Correlation Function. * @@ -14,6 +25,8 @@ import Check from "../../../../Core/Check.js"; * spdcf(delta_t) = A_t * (alpha_t + ((1 - alpha_t)(1 + beta_t)) / (beta_t + e^(delta_t/T_t))) * ``` * + * @constructor + * @param {Spdcf.ConstructorOptions} options An object describing initialization options * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ function Spdcf(options) { From c9100904dc03183ee37fe51b4d3f41cce99f76bf Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 30 Sep 2024 16:03:43 +0200 Subject: [PATCH 38/38] Try to revert undesired formatting changes --- .../3D Tiles Point Cloud Classification.html | 3 +- .../gallery/3D Tiles Point Cloud Shading.html | 3 +- .../3D Tiles Terrain Classification.html | 3 +- .../Custom Per-Feature Post Process.html | 2 +- .../gallery/Custom Post Process.html | 34 +++++----- Apps/Sandcastle/gallery/Fog Post Process.html | 68 +++++++++---------- .../gallery/Google Earth Enterprise.html | 2 +- Apps/Sandcastle/gallery/Materials.html | 22 +++--- .../Scene/ArcGisMapServerImageryProvider.js | 6 +- .../Model/PrimitiveOutlineGeneratorSpec.js | 2 +- .../Specs/Scene/Vector3DTilePointsSpec.js | 2 +- 11 files changed, 75 insertions(+), 72 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Classification.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Classification.html index 13b15e133c6..fedb3b12bb3 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Classification.html +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Classification.html @@ -108,7 +108,8 @@ highlighted.feature = pickedFeature; Cesium.Color.clone(pickedFeature.color, highlighted.originalColor); pickedFeature.color = Cesium.Color.YELLOW.withAlpha(0.5); - }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); //Sandcastle_End + }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + //Sandcastle_End }; if (typeof Cesium !== "undefined") { window.startupCalled = true; diff --git a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Shading.html b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Shading.html index 2241bd0ea04..3d3dc2a6c10 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Point Cloud Shading.html +++ b/Apps/Sandcastle/gallery/3D Tiles Point Cloud Shading.html @@ -345,7 +345,8 @@ if (Cesium.defined(viewModelTileset)) { viewModelTileset.pointCloudShading.eyeDomeLighting = checked; } - }); //Sandcastle_End + }); + //Sandcastle_End }; if (typeof Cesium !== "undefined") { window.startupCalled = true; diff --git a/Apps/Sandcastle/gallery/3D Tiles Terrain Classification.html b/Apps/Sandcastle/gallery/3D Tiles Terrain Classification.html index c336e689959..581814432c7 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Terrain Classification.html +++ b/Apps/Sandcastle/gallery/3D Tiles Terrain Classification.html @@ -80,7 +80,8 @@ highlighted.feature = pickedFeature; Cesium.Color.clone(pickedFeature.color, highlighted.originalColor); pickedFeature.color = Cesium.Color.YELLOW; - }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); //Sandcastle_End + }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); + //Sandcastle_End }; if (typeof Cesium !== "undefined") { window.startupCalled = true; diff --git a/Apps/Sandcastle/gallery/Custom Per-Feature Post Process.html b/Apps/Sandcastle/gallery/Custom Per-Feature Post Process.html index 72cc3f2c001..d08090d9378 100644 --- a/Apps/Sandcastle/gallery/Custom Per-Feature Post Process.html +++ b/Apps/Sandcastle/gallery/Custom Per-Feature Post Process.html @@ -54,7 +54,7 @@ if (czm_selected()) { vec3 highlighted = highlight.a * highlight.rgb + (1.0 - highlight.a) * color.rgb; out_FragColor = vec4(highlighted, 1.0); - } else { + } else { out_FragColor = color; } } diff --git a/Apps/Sandcastle/gallery/Custom Post Process.html b/Apps/Sandcastle/gallery/Custom Post Process.html index 38eeaf856e9..39f5450cb27 100644 --- a/Apps/Sandcastle/gallery/Custom Post Process.html +++ b/Apps/Sandcastle/gallery/Custom Post Process.html @@ -44,23 +44,23 @@ }); const fragmentShaderSource = ` - uniform sampler2D colorTexture; - in vec2 v_textureCoordinates; - const int KERNEL_WIDTH = 16; - void main(void) - { - vec2 step = czm_pixelRatio / czm_viewport.zw; - vec2 integralPos = v_textureCoordinates - mod(v_textureCoordinates, 8.0 * step); - vec3 averageValue = vec3(0.0); - for (int i = 0; i < KERNEL_WIDTH; i++) - { - for (int j = 0; j < KERNEL_WIDTH; j++) - { - averageValue += texture(colorTexture, integralPos + step * vec2(i, j)).rgb; - } - } - averageValue /= float(KERNEL_WIDTH * KERNEL_WIDTH); - out_FragColor = vec4(averageValue, 1.0); + uniform sampler2D colorTexture; + in vec2 v_textureCoordinates; + const int KERNEL_WIDTH = 16; + void main(void) + { + vec2 step = czm_pixelRatio / czm_viewport.zw; + vec2 integralPos = v_textureCoordinates - mod(v_textureCoordinates, 8.0 * step); + vec3 averageValue = vec3(0.0); + for (int i = 0; i < KERNEL_WIDTH; i++) + { + for (int j = 0; j < KERNEL_WIDTH; j++) + { + averageValue += texture(colorTexture, integralPos + step * vec2(i, j)).rgb; + } + } + averageValue /= float(KERNEL_WIDTH * KERNEL_WIDTH); + out_FragColor = vec4(averageValue, 1.0); } `; viewer.scene.postProcessStages.add( diff --git a/Apps/Sandcastle/gallery/Fog Post Process.html b/Apps/Sandcastle/gallery/Fog Post Process.html index d3936751c11..8896ca04c05 100644 --- a/Apps/Sandcastle/gallery/Fog Post Process.html +++ b/Apps/Sandcastle/gallery/Fog Post Process.html @@ -48,40 +48,40 @@ } const fragmentShaderSource = ` - float getDistance(sampler2D depthTexture, vec2 texCoords) - { - float depth = czm_unpackDepth(texture(depthTexture, texCoords)); - if (depth == 0.0) { - return czm_infinity; - } - vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth); - return -eyeCoordinate.z / eyeCoordinate.w; - } - float interpolateByDistance(vec4 nearFarScalar, float distance) - { - float startDistance = nearFarScalar.x; - float startValue = nearFarScalar.y; - float endDistance = nearFarScalar.z; - float endValue = nearFarScalar.w; - float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0); - return mix(startValue, endValue, t); - } - vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor) - { - return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a); - } - uniform sampler2D colorTexture; - uniform sampler2D depthTexture; - uniform vec4 fogByDistance; - uniform vec4 fogColor; - in vec2 v_textureCoordinates; - void main(void) - { - float distance = getDistance(depthTexture, v_textureCoordinates); - vec4 sceneColor = texture(colorTexture, v_textureCoordinates); - float blendAmount = interpolateByDistance(fogByDistance, distance); - vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * blendAmount); - out_FragColor = alphaBlend(finalFogColor, sceneColor); + float getDistance(sampler2D depthTexture, vec2 texCoords) + { + float depth = czm_unpackDepth(texture(depthTexture, texCoords)); + if (depth == 0.0) { + return czm_infinity; + } + vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth); + return -eyeCoordinate.z / eyeCoordinate.w; + } + float interpolateByDistance(vec4 nearFarScalar, float distance) + { + float startDistance = nearFarScalar.x; + float startValue = nearFarScalar.y; + float endDistance = nearFarScalar.z; + float endValue = nearFarScalar.w; + float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0); + return mix(startValue, endValue, t); + } + vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor) + { + return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a); + } + uniform sampler2D colorTexture; + uniform sampler2D depthTexture; + uniform vec4 fogByDistance; + uniform vec4 fogColor; + in vec2 v_textureCoordinates; + void main(void) + { + float distance = getDistance(depthTexture, v_textureCoordinates); + vec4 sceneColor = texture(colorTexture, v_textureCoordinates); + float blendAmount = interpolateByDistance(fogByDistance, distance); + vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * blendAmount); + out_FragColor = alphaBlend(finalFogColor, sceneColor); } `; diff --git a/Apps/Sandcastle/gallery/Google Earth Enterprise.html b/Apps/Sandcastle/gallery/Google Earth Enterprise.html index 115773cdba6..651d2114543 100644 --- a/Apps/Sandcastle/gallery/Google Earth Enterprise.html +++ b/Apps/Sandcastle/gallery/Google Earth Enterprise.html @@ -52,7 +52,7 @@ ); layers.add(blackMarble); } catch (error) { - console.log(`Failed to create Google Earth providers from metadata. Confirm GEE service is correctly configured. + console.log(`Failed to create Google Earth providers from metadata. Confirm GEE service is correctly configured. ${error}`); } diff --git a/Apps/Sandcastle/gallery/Materials.html b/Apps/Sandcastle/gallery/Materials.html index 1640330d4f2..803d899b99a 100644 --- a/Apps/Sandcastle/gallery/Materials.html +++ b/Apps/Sandcastle/gallery/Materials.html @@ -112,19 +112,19 @@ }, }, source: ` - czm_material czm_getMaterial(czm_materialInput materialInput) { - czm_material material = czm_getDefaultMaterial(materialInput); - vec4 color; - float heightValue = texture(heightField, materialInput.st).r; - color.rgb = mix(vec3(0.2, 0.6, 0.2), vec3(1.0, 0.5, 0.2), heightValue); - color.a = (1.0 - texture(image, materialInput.st).r) * 0.7; - color = czm_gammaCorrect(color); - material.diffuse = color.rgb; - material.alpha = color.a; - material.normal = bumpMap.normal; + czm_material czm_getMaterial(czm_materialInput materialInput) { + czm_material material = czm_getDefaultMaterial(materialInput); + vec4 color; + float heightValue = texture(heightField, materialInput.st).r; + color.rgb = mix(vec3(0.2, 0.6, 0.2), vec3(1.0, 0.5, 0.2), heightValue); + color.a = (1.0 - texture(image, materialInput.st).r) * 0.7; + color = czm_gammaCorrect(color); + material.diffuse = color.rgb; + material.alpha = color.a; + material.normal = bumpMap.normal; material.specular = step(0.1, heightValue); // Specular mountain tops material.shininess = 8.0; // Sharpen highlight - return material; + return material; } `, }, diff --git a/packages/engine/Source/Scene/ArcGisMapServerImageryProvider.js b/packages/engine/Source/Scene/ArcGisMapServerImageryProvider.js index f2768fbc57f..d1b97d8f636 100644 --- a/packages/engine/Source/Scene/ArcGisMapServerImageryProvider.js +++ b/packages/engine/Source/Scene/ArcGisMapServerImageryProvider.js @@ -260,9 +260,9 @@ async function requestMetadata(resource, imageryProviderBuilder) { * * Provides tiled imagery hosted by an ArcGIS MapServer. By default, the server's pre-cached tiles are * used, if available. - * + * *
    - * + * * An {@link https://developers.arcgis.com/documentation/mapping-apis-and-services/security| ArcGIS Access Token } is required to authenticate requests to an ArcGIS Image Tile service. * To access secure ArcGIS resources, it's required to create an ArcGIS developer * account or an ArcGIS online account, then implement an authentication method to obtain an access token. @@ -278,7 +278,7 @@ async function requestMetadata(resource, imageryProviderBuilder) { * @example * // Set the default access token for accessing ArcGIS Image Tile service * Cesium.ArcGisMapService.defaultAccessToken = ""; - * + * * // Add a base layer from a default ArcGIS basemap * const viewer = new Cesium.Viewer("cesiumContainer", { * baseLayer: Cesium.ImageryLayer.fromProviderAsync( diff --git a/packages/engine/Specs/Scene/Model/PrimitiveOutlineGeneratorSpec.js b/packages/engine/Specs/Scene/Model/PrimitiveOutlineGeneratorSpec.js index eb73de21290..1e3a0ace79e 100644 --- a/packages/engine/Specs/Scene/Model/PrimitiveOutlineGeneratorSpec.js +++ b/packages/engine/Specs/Scene/Model/PrimitiveOutlineGeneratorSpec.js @@ -263,7 +263,7 @@ describe( 4, 8, 11 ]); // prettier-ignore - let expectedOutlineCoordinates = new Float32Array([ + let expectedOutlineCoordinates = new Float32Array([ 0, 1, 0, 0, 1, 0, 0, 1, 0, diff --git a/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js b/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js index ee9cf1d6d19..b171cc95684 100644 --- a/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js +++ b/packages/engine/Specs/Scene/Vector3DTilePointsSpec.js @@ -431,7 +431,7 @@ describe( /*[ Cesium3DTileStyle option, Cesium3DTileStyle default value, - Cesium3DTileFeature property, + Cesium3DTileFeature property, expected Cesium3DTileFeature value, expected undefined Cesium3DTileFeature value ]*/