UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

407 lines (379 loc) 13 kB
import Check from "../../Core/Check.js"; import defined from "../../Core/defined.js"; import destroyObject from "../../Core/destroyObject.js"; import DeveloperError from "../../Core/DeveloperError.js"; import ImageryConfiguration from "./ImageryConfiguration.js"; import ModelPrimitiveImagery from "./ModelPrimitiveImagery.js"; /** * A class managing the draping of imagery on a <code>Model</code>. * * An instance of this class is created in the Model constructor. It will * create the data structures that carry the information that is required * for mapping imagery textures on model primitives. * * It offers two functions for managing the lifecycle of this draping process: * * The <code>update</code> function is called from the <code>Model.update</code> * function in each frame. It will create one <code>ModelPrimitiveImagery</code> * instance for each primitive that appears in the model, and call the * <code>update</code> function of these instances, respectively. * * The <code>ready</code> getter will be used to determine whether the * draping computations are done, and the update process of the <code>Model</code> * can continue, eventually causing the <code>model.ready</code> flag to * become <code>true</code>. The model imagery counts as "ready" when all * the imagery layers of the model are <code>ready</code>, and all the * <code>ModelPrimitiveImagery</code> instances are <code>ready</code>. * * @private */ class ModelImagery { /** * Creates a new instance * * @param {Model} model The model * @throws {DeveloperError} If the model is not defined */ constructor(model) { //>>includeStart('debug', pragmas.debug); Check.defined("model", model); //>>includeEnd('debug'); /** * The model that this instance was created for. * * @type {Model} * @readonly * @private */ this._model = model; /** * One <code<ModelPrimitiveImagery</code> for each primitive * that appears in the model. * * Initially, this is <code>undefined</code>. When the <code>update</code> * function is called and all imagery layers that are associated with the * model are <code>ready</code>, this is initialized with one instance * of a <code>ModelPrimitiveImagery</code> per runtime primitive (i.e. one for * each <code>model.sceneGraph._runtimeNodes[n]._runtimePrimitives[p]</code>) * * @type {ModelPrimitiveImagery[]|undefined} * @private */ this._modelPrimitiveImageries = undefined; /** * One <code>ImageryConfiguration</code> object for each <code>ImageryLayer</code> * that is associated with the model. * * This is used for determining whether the configuration (relevant property * values) of an imagery layer has been changed since the previous * <code>update</code> call, which should cause the draw commands of the * model to be reset. * * @type {ImageryConfiguration[]} * @private */ this._imageryConfigurations = []; } /** * The update function that is called from <code>Model.update</code> in * each frame. * * This checks whether the imagery layer objects that are associated * with the model are all <code>ready</code>. If they are not yet * ready, then nothing is done. * * Otherwise, this just calls the <code>update</code> function of * the <code>_modelPrimitiveImageries</code> (creating them if they had * not been created yet). * * @param {FrameState} frameState The frame state */ update(frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); if (!this._hasImagery) { return; } if (!this._allImageryLayersReady) { return; } if (!defined(this._modelPrimitiveImageries)) { this._modelPrimitiveImageries = this._createModelPrimitiveImageries(); } this._updateModelPrimitiveImageries(frameState); this._checkForModifiedImageryConfigurations(); } /** * Creates the <code>ModelPrimitiveImagery</code> array that contains * one <code>ModelPrimitiveImagery</code> for each primitive that is * contained in the model. * * @returns {ModelPrimitiveImagery[]} The model primitive imageries * @private */ _createModelPrimitiveImageries() { const model = this._model; const runtimeNodesAndPrimitives = this._collectRuntimeNodesAndPrimitives(); const modelPrimitiveImageries = []; const length = runtimeNodesAndPrimitives.length; for (let i = 0; i < length; i++) { const runtimeNodeAndPrimitive = runtimeNodesAndPrimitives[i]; const runtimeNode = runtimeNodeAndPrimitive.runtimeNode; const runtimePrimitive = runtimeNodeAndPrimitive.runtimePrimitive; const modelPrimitiveImagery = new ModelPrimitiveImagery( model, runtimeNode, runtimePrimitive, ); runtimePrimitive.primitive.modelPrimitiveImagery = modelPrimitiveImagery; modelPrimitiveImageries.push(modelPrimitiveImagery); } return modelPrimitiveImageries; } /** * Computes all runtime nodes and primitives of the model. * * This is just the array that contains a * <code>{ runtimeNode, runtimePrimitive }</code> * for each * <code>model.sceneGraph._runtimeNodes[n]._runtimePrimitives[p]</code>. * * @returns {object[]} The runtime nodes and primitives * @private */ _collectRuntimeNodesAndPrimitives() { const model = this._model; const sceneGraph = model.sceneGraph; const runtimeNodes = sceneGraph._runtimeNodes; const runtimeNodesAndPrimitives = []; for (let i = 0; i < runtimeNodes.length; i++) { const runtimeNode = runtimeNodes[i]; if (!defined(runtimeNode)) { continue; } for (let j = 0; j < runtimeNode.runtimePrimitives.length; j++) { const runtimePrimitive = runtimeNode.runtimePrimitives[j]; runtimeNodesAndPrimitives.push({ runtimeNode: runtimeNode, runtimePrimitive: runtimePrimitive, }); } } return runtimeNodesAndPrimitives; } /** * Just calls <code>update</code> on each <code>ModelPrimitiveImagery</code> * as part of the <code>update</code> of this class. * * @private */ _updateModelPrimitiveImageries(frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); if (!defined(this._modelPrimitiveImageries)) { throw new DeveloperError( "The modelPrimitiveImageries have not been created", ); } const modelPrimitiveImageries = this._modelPrimitiveImageries; const length = modelPrimitiveImageries.length; for (let i = 0; i < length; i++) { const modelPrimitiveImagery = modelPrimitiveImageries[i]; modelPrimitiveImagery.update(frameState); } } /** * Destroy and delete all <code>ModelPrimitiveImagery</code> instances * if they already have been created. */ _deleteModelPrimitiveImageries() { const modelPrimitiveImageries = this._modelPrimitiveImageries; if (!defined(modelPrimitiveImageries)) { return; } const length = modelPrimitiveImageries.length; for (let i = 0; i < length; i++) { const modelPrimitiveImagery = modelPrimitiveImageries[i]; modelPrimitiveImagery.destroy(); } delete this._modelPrimitiveImageries; } /** * Returns whether this instance is "ready". * * This means that all imagery layers that are associated with the model * are <code>ready</code>, and all <code>ModelPrimitiveImagery</code> * instances are <code>ready</code>. * * When this is <code>true</code>, then the mapping computations are * complete and the structures containing the mapping information have * been initialized. Otherwise, subsequent calls to <code>update</code> * will perform the necessary computation until this getter eventually * returns <code>true</code>. * * @returns {boolean} Whether this instance is "ready" */ get ready() { if (!this._hasImagery) { return true; } if (!this._allImageryLayersReady) { return false; } if (!this._allModelPrimitiveImageriesReady) { return false; } return true; } /** * Returns whether the model has imagery layers associated with it. * * @private */ get _hasImagery() { const model = this._model; const imageryLayers = model.imageryLayers; return defined(imageryLayers) && imageryLayers.length > 0; } /** * Returns whether all imagery layers that are associated with the * model are <code>ready</code>. * * If the model does not have imagery, then this always returns * <code>true</code>. Otherwise, it returns whether each imagery * layer is <code>ready</code>. * * @private */ get _allImageryLayersReady() { if (!this._hasImagery) { return true; } const imageryLayers = this._model.imageryLayers; const length = imageryLayers.length; for (let i = 0; i < length; i++) { const imageryLayer = imageryLayers.get(i); if (!imageryLayer.ready) { return false; } } return true; } /** * Returns whether all <code>ModelPrimitiveImagery</code> instances * are are <code>ready</code>. * * @private */ get _allModelPrimitiveImageriesReady() { const modelPrimitiveImageries = this._modelPrimitiveImageries; if (!defined(modelPrimitiveImageries)) { return false; } const length = modelPrimitiveImageries.length; for (let i = 0; i < length; i++) { const modelPrimitiveImagery = modelPrimitiveImageries[i]; if (!modelPrimitiveImagery.ready) { return false; } } return true; } /** * Check whether any of the settings of any imagery layer (like alpha * or hue) has been changed since the last call to the <code>update</code> * function. * * If this is the case, the draw commands of the model will be reset. */ _checkForModifiedImageryConfigurations() { if (this._imageryConfigurationsModified()) { this._updateImageryConfigurations(); const model = this._model; model.resetDrawCommands(); } } /** * Returns whether any setting of an imagery layer (like alpha or hue) has * been changed since the last time the <code>ImageryConfiguration</code> * objects have been updated. * * @returns {boolean} Whether there was a modification */ _imageryConfigurationsModified() { const model = this._model; const imageryLayers = model.imageryLayers; const imageryConfigurations = this._imageryConfigurations; if (imageryLayers.length !== imageryConfigurations.length) { return true; } for (let i = 0; i < imageryLayers.length; i++) { const imageryLayer = imageryLayers.get(i); const imageryConfiguration = imageryConfigurations[i]; if (imageryLayer.show !== imageryConfiguration.show) { return true; } if (imageryLayer.alpha !== imageryConfiguration.alpha) { return true; } if (imageryLayer.brightness !== imageryConfiguration.brightness) { return true; } if (imageryLayer.contrast !== imageryConfiguration.contrast) { return true; } if (imageryLayer.hue !== imageryConfiguration.hue) { return true; } if (imageryLayer.saturation !== imageryConfiguration.saturation) { return true; } if (imageryLayer.gamma !== imageryConfiguration.gamma) { return true; } if (imageryLayer.colorToAlpha !== imageryConfiguration.colorToAlpha) { return true; } } return false; } /** * Create one <code>ImageryConfiguration</code> object for each imagery * layer that appears in the model, and store them as the * <code>_imageryConfigurations</code>. */ _updateImageryConfigurations() { const model = this._model; const imageryLayers = model.imageryLayers; const imageryConfigurations = this._imageryConfigurations; imageryConfigurations.length = imageryLayers.length; for (let i = 0; i < imageryLayers.length; i++) { const imageryLayer = imageryLayers.get(i); imageryConfigurations[i] = new ImageryConfiguration(imageryLayer); } } /** * Returns whether this object was destroyed. * * If this object was destroyed, calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError}. * * @returns {boolean} Whether this object was destroyed */ isDestroyed() { return false; } /** * Destroys this object and all its resources. */ destroy() { if (this.isDestroyed()) { return; } this._deleteModelPrimitiveImageries(); return destroyObject(this); } } export default ModelImagery;