mirror of https://github.com/CesiumGS/cesium.git
411 lines
13 KiB
JavaScript
411 lines
13 KiB
JavaScript
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) {
|
|
// When there is no imagery, make sure to delete any model primitive
|
|
// imageries that may previously have been created
|
|
this._deleteModelPrimitiveImageries();
|
|
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;
|
|
this._model.resetDrawCommands();
|
|
}
|
|
|
|
/**
|
|
* 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;
|