cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
1,398 lines (1,290 loc) • 191 kB
JavaScript
import BoundingSphere from "../Core/BoundingSphere.js";
import Cartesian2 from "../Core/Cartesian2.js";
import Cartesian3 from "../Core/Cartesian3.js";
import Cartesian4 from "../Core/Cartesian4.js";
import Cartographic from "../Core/Cartographic.js";
import Check from "../Core/Check.js";
import clone from "../Core/clone.js";
import Color from "../Core/Color.js";
import combine from "../Core/combine.js";
import createGuid from "../Core/createGuid.js";
import Credit from "../Core/Credit.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";
import FeatureDetection from "../Core/FeatureDetection.js";
import getAbsoluteUri from "../Core/getAbsoluteUri.js";
import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
import getMagic from "../Core/getMagic.js";
import getStringFromTypedArray from "../Core/getStringFromTypedArray.js";
import IndexDatatype from "../Core/IndexDatatype.js";
import loadCRN from "../Core/loadCRN.js";
import loadImageFromTypedArray from "../Core/loadImageFromTypedArray.js";
import loadKTX from "../Core/loadKTX.js";
import CesiumMath from "../Core/Math.js";
import Matrix3 from "../Core/Matrix3.js";
import Matrix4 from "../Core/Matrix4.js";
import PixelFormat from "../Core/PixelFormat.js";
import PrimitiveType from "../Core/PrimitiveType.js";
import Quaternion from "../Core/Quaternion.js";
import Resource from "../Core/Resource.js";
import Transforms from "../Core/Transforms.js";
import WebGLConstants from "../Core/WebGLConstants.js";
import Buffer from "../Renderer/Buffer.js";
import BufferUsage from "../Renderer/BufferUsage.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import Pass from "../Renderer/Pass.js";
import RenderState from "../Renderer/RenderState.js";
import Sampler from "../Renderer/Sampler.js";
import ShaderProgram from "../Renderer/ShaderProgram.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import Texture from "../Renderer/Texture.js";
import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
import TextureWrap from "../Renderer/TextureWrap.js";
import VertexArray from "../Renderer/VertexArray.js";
import addDefaults from "../ThirdParty/GltfPipeline/addDefaults.js";
import addPipelineExtras from "../ThirdParty/GltfPipeline/addPipelineExtras.js";
import ForEach from "../ThirdParty/GltfPipeline/ForEach.js";
import getAccessorByteStride from "../ThirdParty/GltfPipeline/getAccessorByteStride.js";
import hasExtension from "../ThirdParty/GltfPipeline/hasExtension.js";
import numberOfComponentsForType from "../ThirdParty/GltfPipeline/numberOfComponentsForType.js";
import parseGlb from "../ThirdParty/GltfPipeline/parseGlb.js";
import updateVersion from "../ThirdParty/GltfPipeline/updateVersion.js";
import when from "../ThirdParty/when.js";
import Axis from "./Axis.js";
import BlendingState from "./BlendingState.js";
import ClippingPlaneCollection from "./ClippingPlaneCollection.js";
import ColorBlendMode from "./ColorBlendMode.js";
import DepthFunction from "./DepthFunction.js";
import DracoLoader from "./DracoLoader.js";
import getClipAndStyleCode from "./getClipAndStyleCode.js";
import getClippingFunction from "./getClippingFunction.js";
import HeightReference from "./HeightReference.js";
import JobType from "./JobType.js";
import ModelAnimationCache from "./ModelAnimationCache.js";
import ModelAnimationCollection from "./ModelAnimationCollection.js";
import ModelLoadResources from "./ModelLoadResources.js";
import ModelMaterial from "./ModelMaterial.js";
import ModelMesh from "./ModelMesh.js";
import ModelNode from "./ModelNode.js";
import ModelOutlineLoader from "./ModelOutlineLoader.js";
import ModelUtility from "./ModelUtility.js";
import OctahedralProjectedCubeMap from "./OctahedralProjectedCubeMap.js";
import processModelMaterialsCommon from "./processModelMaterialsCommon.js";
import processPbrMaterials from "./processPbrMaterials.js";
import SceneMode from "./SceneMode.js";
import ShadowMode from "./ShadowMode.js";
import StencilConstants from "./StencilConstants.js";
var boundingSphereCartesian3Scratch = new Cartesian3();
var ModelState = ModelUtility.ModelState;
// glTF MIME types discussed in https://github.com/KhronosGroup/glTF/issues/412 and https://github.com/KhronosGroup/glTF/issues/943
var defaultModelAccept =
"model/gltf-binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01";
var articulationEpsilon = CesiumMath.EPSILON16;
///////////////////////////////////////////////////////////////////////////
function setCachedGltf(model, cachedGltf) {
model._cachedGltf = cachedGltf;
}
// glTF JSON can be big given embedded geometry, textures, and animations, so we
// cache it across all models using the same url/cache-key. This also reduces the
// slight overhead in assigning defaults to missing values.
//
// Note that this is a global cache, compared to renderer resources, which
// are cached per context.
function CachedGltf(options) {
this._gltf = options.gltf;
this.ready = options.ready;
this.modelsToLoad = [];
this.count = 0;
}
Object.defineProperties(CachedGltf.prototype, {
gltf: {
set: function (value) {
this._gltf = value;
},
get: function () {
return this._gltf;
},
},
});
CachedGltf.prototype.makeReady = function (gltfJson) {
this.gltf = gltfJson;
var models = this.modelsToLoad;
var length = models.length;
for (var i = 0; i < length; ++i) {
var m = models[i];
if (!m.isDestroyed()) {
setCachedGltf(m, this);
}
}
this.modelsToLoad = undefined;
this.ready = true;
};
var gltfCache = {};
var uriToGuid = {};
///////////////////////////////////////////////////////////////////////////
/**
* A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL.
* <p>
* Cesium includes support for geometry and materials, glTF animations, and glTF skinning.
* In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable
* with {@link Model#getNode}. glTF cameras and lights are not currently supported.
* </p>
* <p>
* An external glTF asset is created with {@link Model.fromGltf}. glTF JSON can also be
* created at runtime and passed to this constructor function. In either case, the
* {@link Model#readyPromise} is resolved when the model is ready to render, i.e.,
* when the external binary, image, and shader files are downloaded and the WebGL
* resources are created.
* </p>
* <p>
* Cesium supports glTF assets with the following extensions:
* <ul>
* <li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform}
* </li>
* </ul>
* </p>
* <p>
* For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the
* CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
* relative to a local origin.
* </p>
*
* @alias Model
* @constructor
*
* @param {Object} [options] Object with the following properties:
* @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer.
* @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
* @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
* @param {Number} [options.scale=1.0] A uniform scale applied to this model.
* @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
* @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
* @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
* @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
* @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
* @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
* @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
* @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain.
* @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
* @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
* @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color.
* @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
* @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
* @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts.
* @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
* @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models.
* @param {Cartesian2} [options.imageBasedLightingFactor=Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox.
* @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
* @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
* @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
* @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
* @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
* @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0.
*
* @see Model.fromGltf
*
* @demo {@link https://sandcastle.cesium.com/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo}
*/
function Model(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var cacheKey = options.cacheKey;
this._cacheKey = cacheKey;
this._cachedGltf = undefined;
this._releaseGltfJson = defaultValue(options.releaseGltfJson, false);
var cachedGltf;
if (
defined(cacheKey) &&
defined(gltfCache[cacheKey]) &&
gltfCache[cacheKey].ready
) {
// glTF JSON is in cache and ready
cachedGltf = gltfCache[cacheKey];
++cachedGltf.count;
} else {
// glTF was explicitly provided, e.g., when a user uses the Model constructor directly
var gltf = options.gltf;
if (defined(gltf)) {
if (gltf instanceof ArrayBuffer) {
gltf = new Uint8Array(gltf);
}
if (gltf instanceof Uint8Array) {
// Binary glTF
var parsedGltf = parseGlb(gltf);
cachedGltf = new CachedGltf({
gltf: parsedGltf,
ready: true,
});
} else {
// Normal glTF (JSON)
cachedGltf = new CachedGltf({
gltf: options.gltf,
ready: true,
});
}
cachedGltf.count = 1;
if (defined(cacheKey)) {
gltfCache[cacheKey] = cachedGltf;
}
}
}
setCachedGltf(this, cachedGltf);
var basePath = defaultValue(options.basePath, "");
this._resource = Resource.createIfNeeded(basePath);
// User specified credit
var credit = options.credit;
if (typeof credit === "string") {
credit = new Credit(credit);
}
this._credit = credit;
// Create a list of Credit's so they can be added from the Resource later
this._resourceCredits = [];
/**
* Determines if the model primitive will be shown.
*
* @type {Boolean}
*
* @default true
*/
this.show = defaultValue(options.show, true);
/**
* The silhouette color.
*
* @type {Color}
*
* @default Color.RED
*/
this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED);
this._silhouetteColor = new Color();
this._silhouetteColorPreviousAlpha = 1.0;
this._normalAttributeName = undefined;
/**
* The size of the silhouette in pixels.
*
* @type {Number}
*
* @default 0.0
*/
this.silhouetteSize = defaultValue(options.silhouetteSize, 0.0);
/**
* The 4x4 transformation matrix that transforms the model from model to world coordinates.
* When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
* Local reference frames can be used by providing a different transformation matrix, like that returned
* by {@link Transforms.eastNorthUpToFixedFrame}.
*
* @type {Matrix4}
*
* @default {@link Matrix4.IDENTITY}
*
* @example
* var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
* m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
*/
this.modelMatrix = Matrix4.clone(
defaultValue(options.modelMatrix, Matrix4.IDENTITY)
);
this._modelMatrix = Matrix4.clone(this.modelMatrix);
this._clampedModelMatrix = undefined;
/**
* A uniform scale applied to this model before the {@link Model#modelMatrix}.
* Values greater than <code>1.0</code> increase the size of the model; values
* less than <code>1.0</code> decrease.
*
* @type {Number}
*
* @default 1.0
*/
this.scale = defaultValue(options.scale, 1.0);
this._scale = this.scale;
/**
* The approximate minimum pixel size of the model regardless of zoom.
* This can be used to ensure that a model is visible even when the viewer
* zooms out. When <code>0.0</code>, no minimum size is enforced.
*
* @type {Number}
*
* @default 0.0
*/
this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);
this._minimumPixelSize = this.minimumPixelSize;
/**
* The maximum scale size for a model. This can be used to give
* an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model
* is never an unreasonable scale.
*
* @type {Number}
*/
this.maximumScale = options.maximumScale;
this._maximumScale = this.maximumScale;
/**
* User-defined object returned when the model is picked.
*
* @type Object
*
* @default undefined
*
* @see Scene#pick
*/
this.id = options.id;
this._id = options.id;
/**
* Returns the height reference of the model
*
* @type {HeightReference}
*
* @default HeightReference.NONE
*/
this.heightReference = defaultValue(
options.heightReference,
HeightReference.NONE
);
this._heightReference = this.heightReference;
this._heightChanged = false;
this._removeUpdateHeightCallback = undefined;
var scene = options.scene;
this._scene = scene;
if (defined(scene) && defined(scene.terrainProviderChanged)) {
this._terrainProviderChangedCallback = scene.terrainProviderChanged.addEventListener(
function () {
this._heightChanged = true;
},
this
);
}
/**
* Used for picking primitives that wrap a model.
*
* @private
*/
this._pickObject = options.pickObject;
this._allowPicking = defaultValue(options.allowPicking, true);
this._ready = false;
this._readyPromise = when.defer();
/**
* The currently playing glTF animations.
*
* @type {ModelAnimationCollection}
*/
this.activeAnimations = new ModelAnimationCollection(this);
/**
* Determines if the model's animations should hold a pose over frames where no keyframes are specified.
*
* @type {Boolean}
*/
this.clampAnimations = defaultValue(options.clampAnimations, true);
this._defaultTexture = undefined;
this._incrementallyLoadTextures = defaultValue(
options.incrementallyLoadTextures,
true
);
this._asynchronous = defaultValue(options.asynchronous, true);
/**
* Determines whether the model casts or receives shadows from light sources.
*
* @type {ShadowMode}
*
* @default ShadowMode.ENABLED
*/
this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
this._shadows = this.shadows;
/**
* A color that blends with the model's rendered color.
*
* @type {Color}
*
* @default Color.WHITE
*/
this.color = Color.clone(defaultValue(options.color, Color.WHITE));
this._colorPreviousAlpha = 1.0;
/**
* Defines how the color blends with the model.
*
* @type {ColorBlendMode}
*
* @default ColorBlendMode.HIGHLIGHT
*/
this.colorBlendMode = defaultValue(
options.colorBlendMode,
ColorBlendMode.HIGHLIGHT
);
/**
* Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>.
* A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with
* any value in-between resulting in a mix of the two.
*
* @type {Number}
*
* @default 0.5
*/
this.colorBlendAmount = defaultValue(options.colorBlendAmount, 0.5);
this._colorShadingEnabled = false;
this._clippingPlanes = undefined;
this.clippingPlanes = options.clippingPlanes;
// Used for checking if shaders need to be regenerated due to clipping plane changes.
this._clippingPlanesState = 0;
// If defined, use this matrix to transform miscellaneous properties like
// clipping planes and IBL instead of the modelMatrix. This is so that when
// models are part of a tileset these properties get transformed relative to
// a common reference (such as the root).
this.referenceMatrix = undefined;
/**
* Whether to cull back-facing geometry. When true, back face culling is
* determined by the material's doubleSided property; when false, back face
* culling is disabled. Back faces are not culled if {@link Model#color} is
* translucent or {@link Model#silhouetteSize} is greater than 0.0.
*
* @type {Boolean}
*
* @default true
*/
this.backFaceCulling = defaultValue(options.backFaceCulling, true);
/**
* This property is for debugging only; it is not for production use nor is it optimized.
* <p>
* Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds
* to one draw command. A glTF mesh has an array of primitives, often of length one.
* </p>
*
* @type {Boolean}
*
* @default false
*/
this.debugShowBoundingVolume = defaultValue(
options.debugShowBoundingVolume,
false
);
this._debugShowBoundingVolume = false;
/**
* This property is for debugging only; it is not for production use nor is it optimized.
* <p>
* Draws the model in wireframe.
* </p>
*
* @type {Boolean}
*
* @default false
*/
this.debugWireframe = defaultValue(options.debugWireframe, false);
this._debugWireframe = false;
this._distanceDisplayCondition = options.distanceDisplayCondition;
// Undocumented options
this._addBatchIdToGeneratedShaders = options.addBatchIdToGeneratedShaders;
this._precreatedAttributes = options.precreatedAttributes;
this._vertexShaderLoaded = options.vertexShaderLoaded;
this._fragmentShaderLoaded = options.fragmentShaderLoaded;
this._uniformMapLoaded = options.uniformMapLoaded;
this._pickIdLoaded = options.pickIdLoaded;
this._ignoreCommands = defaultValue(options.ignoreCommands, false);
this._requestType = options.requestType;
this._upAxis = defaultValue(options.upAxis, Axis.Y);
this._gltfForwardAxis = Axis.Z;
this._forwardAxis = options.forwardAxis;
/**
* @private
* @readonly
*/
this.cull = defaultValue(options.cull, true);
/**
* @private
* @readonly
*/
this.opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale
this._clippingPlanesMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from reference matrix and the current view matrix
this._iblReferenceFrameMatrix = Matrix3.clone(Matrix3.IDENTITY); // Derived from reference matrix and the current view matrix
this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
this._boundingSphere = undefined;
this._scaledBoundingSphere = new BoundingSphere();
this._state = ModelState.NEEDS_LOAD;
this._loadResources = undefined;
this._mode = undefined;
this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property
this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform
this._dirty = false; // true when the model was transformed this frame
this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal
this._runtime = {
animations: undefined,
articulationsByName: undefined,
articulationsByStageKey: undefined,
stagesByKey: undefined,
rootNodes: undefined,
nodes: undefined, // Indexed with the node's index
nodesByName: undefined, // Indexed with name property in the node
skinnedNodes: undefined,
meshesByName: undefined, // Indexed with the name property in the mesh
materialsByName: undefined, // Indexed with the name property in the material
materialsById: undefined, // Indexed with the material's index
};
this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation
this._extensionsUsed = undefined; // Cached used glTF extensions
this._extensionsRequired = undefined; // Cached required glTF extensions
this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes
this._programPrimitives = {};
this._rendererResources = {
// Cached between models with the same url/cache-key
buffers: {},
vertexArrays: {},
programs: {},
sourceShaders: {},
silhouettePrograms: {},
textures: {},
samplers: {},
renderStates: {},
};
this._cachedRendererResources = undefined;
this._loadRendererResourcesFromCache = false;
this._dequantizeInShader = defaultValue(options.dequantizeInShader, true);
this._decodedData = {};
this._cachedGeometryByteLength = 0;
this._cachedTexturesByteLength = 0;
this._geometryByteLength = 0;
this._texturesByteLength = 0;
this._trianglesLength = 0;
this._pointsLength = 0;
// Hold references for shader reconstruction.
// Hold these separately because _cachedGltf may get released (this.releaseGltfJson)
this._sourceTechniques = {};
this._sourcePrograms = {};
this._quantizedVertexShaders = {};
this._nodeCommands = [];
this._pickIds = [];
// CESIUM_RTC extension
this._rtcCenter = undefined; // reference to either 3D or 2D
this._rtcCenterEye = undefined; // in eye coordinates
this._rtcCenter3D = undefined; // in world coordinates
this._rtcCenter2D = undefined; // in projected world coordinates
this._sourceVersion = undefined;
this._sourceKHRTechniquesWebGL = undefined;
this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0);
Cartesian2.clone(
options.imageBasedLightingFactor,
this._imageBasedLightingFactor
);
this._lightColor = Cartesian3.clone(options.lightColor);
this._luminanceAtZenith = undefined;
this.luminanceAtZenith = defaultValue(options.luminanceAtZenith, 0.2);
this._sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients;
this._specularEnvironmentMaps = options.specularEnvironmentMaps;
this._shouldUpdateSpecularMapAtlas = true;
this._specularEnvironmentMapAtlas = undefined;
this._useDefaultSphericalHarmonics = false;
this._useDefaultSpecularMaps = false;
this._shouldRegenerateShaders = false;
}
Object.defineProperties(Model.prototype, {
/**
* The object for the glTF JSON, including properties with default values omitted
* from the JSON provided to this model.
*
* @memberof Model.prototype
*
* @type {Object}
* @readonly
*
* @default undefined
*/
gltf: {
get: function () {
return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined;
},
},
/**
* When <code>true</code>, the glTF JSON is not stored with the model once the model is
* loaded (when {@link Model#ready} is <code>true</code>). This saves memory when
* geometry, textures, and animations are embedded in the .gltf file.
* This is especially useful for cases like 3D buildings, where each .gltf model is unique
* and caching the glTF JSON is not effective.
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @default false
*
* @private
*/
releaseGltfJson: {
get: function () {
return this._releaseGltfJson;
},
},
/**
* The key identifying this model in the model cache for glTF JSON, renderer resources, and animations.
* Caching saves memory and improves loading speed when several models with the same url are created.
* <p>
* This key is automatically generated when the model is created with {@link Model.fromGltf}. If the model
* is created directly from glTF JSON using the {@link Model} constructor, this key can be manually
* provided; otherwise, the model will not be changed.
* </p>
*
* @memberof Model.prototype
*
* @type {String}
* @readonly
*
* @private
*/
cacheKey: {
get: function () {
return this._cacheKey;
},
},
/**
* The base path that paths in the glTF JSON are relative to. The base
* path is the same path as the path containing the .gltf file
* minus the .gltf file, when binary, image, and shader files are
* in the same directory as the .gltf. When this is <code>''</code>,
* the app's base path is used.
*
* @memberof Model.prototype
*
* @type {String}
* @readonly
*
* @default ''
*/
basePath: {
get: function () {
return this._resource.url;
},
},
/**
* The model's bounding sphere in its local coordinate system. This does not take into
* account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}.
*
* @memberof Model.prototype
*
* @type {BoundingSphere}
* @readonly
*
* @default undefined
*
* @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
*
* @example
* // Center in WGS84 coordinates
* var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
*/
boundingSphere: {
get: function () {
//>>includeStart('debug', pragmas.debug);
if (this._state !== ModelState.LOADED) {
throw new DeveloperError(
"The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true."
);
}
//>>includeEnd('debug');
var modelMatrix = this.modelMatrix;
if (
this.heightReference !== HeightReference.NONE &&
this._clampedModelMatrix
) {
modelMatrix = this._clampedModelMatrix;
}
var nonUniformScale = Matrix4.getScale(
modelMatrix,
boundingSphereCartesian3Scratch
);
var scale = defined(this.maximumScale)
? Math.min(this.maximumScale, this.scale)
: this.scale;
Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale);
var scaledBoundingSphere = this._scaledBoundingSphere;
scaledBoundingSphere.center = Cartesian3.multiplyComponents(
this._boundingSphere.center,
nonUniformScale,
scaledBoundingSphere.center
);
scaledBoundingSphere.radius =
Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;
if (defined(this._rtcCenter)) {
Cartesian3.add(
this._rtcCenter,
scaledBoundingSphere.center,
scaledBoundingSphere.center
);
}
return scaledBoundingSphere;
},
},
/**
* When <code>true</code>, this model is ready to render, i.e., the external binary, image,
* and shader files were downloaded and the WebGL resources were created. This is set to
* <code>true</code> right before {@link Model#readyPromise} is resolved.
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @default false
*/
ready: {
get: function () {
return this._ready;
},
},
/**
* Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,
* and shader files were downloaded and the WebGL resources were created.
* <p>
* This promise is resolved at the end of the frame before the first frame the model is rendered in.
* </p>
*
* @memberof Model.prototype
* @type {Promise.<Model>}
* @readonly
*
* @example
* // Play all animations at half-speed when the model is ready to render
* Cesium.when(model.readyPromise).then(function(model) {
* model.activeAnimations.addAll({
* multiplier : 0.5
* });
* }).otherwise(function(error){
* window.alert(error);
* });
*
* @see Model#ready
*/
readyPromise: {
get: function () {
return this._readyPromise.promise;
},
},
/**
* Determines if model WebGL resource creation will be spread out over several frames or
* block until completion once all glTF files are loaded.
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @default true
*/
asynchronous: {
get: function () {
return this._asynchronous;
},
},
/**
* When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @default true
*/
allowPicking: {
get: function () {
return this._allowPicking;
},
},
/**
* Determine if textures may continue to stream in after the model is loaded.
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @default true
*/
incrementallyLoadTextures: {
get: function () {
return this._incrementallyLoadTextures;
},
},
/**
* Return the number of pending texture loads.
*
* @memberof Model.prototype
*
* @type {Number}
* @readonly
*/
pendingTextureLoads: {
get: function () {
return defined(this._loadResources)
? this._loadResources.pendingTextureLoads
: 0;
},
},
/**
* Returns true if the model was transformed this frame
*
* @memberof Model.prototype
*
* @type {Boolean}
* @readonly
*
* @private
*/
dirty: {
get: function () {
return this._dirty;
},
},
/**
* Gets or sets the condition specifying at what distance from the camera that this model will be displayed.
* @memberof Model.prototype
* @type {DistanceDisplayCondition}
* @default undefined
*/
distanceDisplayCondition: {
get: function () {
return this._distanceDisplayCondition;
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
if (defined(value) && value.far <= value.near) {
throw new DeveloperError("far must be greater than near");
}
//>>includeEnd('debug');
this._distanceDisplayCondition = DistanceDisplayCondition.clone(
value,
this._distanceDisplayCondition
);
},
},
extensionsUsed: {
get: function () {
if (!defined(this._extensionsUsed)) {
this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf);
}
return this._extensionsUsed;
},
},
extensionsRequired: {
get: function () {
if (!defined(this._extensionsRequired)) {
this._extensionsRequired = ModelUtility.getRequiredExtensions(
this.gltf
);
}
return this._extensionsRequired;
},
},
/**
* Gets the model's up-axis.
* By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.
*
* @memberof Model.prototype
*
* @type {Number}
* @default Axis.Y
* @readonly
*
* @private
*/
upAxis: {
get: function () {
return this._upAxis;
},
},
/**
* Gets the model's forward axis.
* By default, glTF 2.0 models are z-forward according to the glTF spec, however older
* glTF (1.0, 0.8) models used x-forward. Note that only Axis.X and Axis.Z are supported.
*
* @memberof Model.prototype
*
* @type {Number}
* @default Axis.Z
* @readonly
*
* @private
*/
forwardAxis: {
get: function () {
if (defined(this._forwardAxis)) {
return this._forwardAxis;
}
return this._gltfForwardAxis;
},
},
/**
* Gets the model's triangle count.
*
* @private
*/
trianglesLength: {
get: function () {
return this._trianglesLength;
},
},
/**
* Gets the model's point count.
*
* @private
*/
pointsLength: {
get: function () {
return this._pointsLength;
},
},
/**
* Gets the model's geometry memory in bytes. This includes all vertex and index buffers.
*
* @private
*/
geometryByteLength: {
get: function () {
return this._geometryByteLength;
},
},
/**
* Gets the model's texture memory in bytes.
*
* @private
*/
texturesByteLength: {
get: function () {
return this._texturesByteLength;
},
},
/**
* Gets the model's cached geometry memory in bytes. This includes all vertex and index buffers.
*
* @private
*/
cachedGeometryByteLength: {
get: function () {
return this._cachedGeometryByteLength;
},
},
/**
* Gets the model's cached texture memory in bytes.
*
* @private
*/
cachedTexturesByteLength: {
get: function () {
return this._cachedTexturesByteLength;
},
},
/**
* The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
*
* @memberof Model.prototype
*
* @type {ClippingPlaneCollection}
*/
clippingPlanes: {
get: function () {
return this._clippingPlanes;
},
set: function (value) {
if (value === this._clippingPlanes) {
return;
}
// Handle destroying, checking of unknown, checking for existing ownership
ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");
},
},
/**
* @private
*/
pickIds: {
get: function () {
return this._pickIds;
},
},
/**
* Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final
* diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources.
*
* @memberof Model.prototype
*
* @type {Cartesian2}
* @default Cartesian2(1.0, 1.0)
*/
imageBasedLightingFactor: {
get: function () {
return this._imageBasedLightingFactor;
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("imageBasedLightingFactor", value);
Check.typeOf.number.greaterThanOrEquals(
"imageBasedLightingFactor.x",
value.x,
0.0
);
Check.typeOf.number.lessThanOrEquals(
"imageBasedLightingFactor.x",
value.x,
1.0
);
Check.typeOf.number.greaterThanOrEquals(
"imageBasedLightingFactor.y",
value.y,
0.0
);
Check.typeOf.number.lessThanOrEquals(
"imageBasedLightingFactor.y",
value.y,
1.0
);
//>>includeEnd('debug');
var imageBasedLightingFactor = this._imageBasedLightingFactor;
if (
value === imageBasedLightingFactor ||
Cartesian2.equals(value, imageBasedLightingFactor)
) {
return;
}
this._shouldRegenerateShaders =
this._shouldRegenerateShaders ||
(this._imageBasedLightingFactor.x > 0.0 && value.x === 0.0) ||
(this._imageBasedLightingFactor.x === 0.0 && value.x > 0.0);
this._shouldRegenerateShaders =
this._shouldRegenerateShaders ||
(this._imageBasedLightingFactor.y > 0.0 && value.y === 0.0) ||
(this._imageBasedLightingFactor.y === 0.0 && value.y > 0.0);
Cartesian2.clone(value, this._imageBasedLightingFactor);
},
},
/**
* The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
* <p>
* For example, disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cesium.Cartesian2(0.0, 0.0)</code> will make the
* model much darker. Here, increasing the intensity of the light source will make the model brighter.
* </p>
*
* @memberof Model.prototype
*
* @type {Cartesian3}
* @default undefined
*/
lightColor: {
get: function () {
return this._lightColor;
},
set: function (value) {
var lightColor = this._lightColor;
if (value === lightColor || Cartesian3.equals(value, lightColor)) {
return;
}
this._shouldRegenerateShaders =
this._shouldRegenerateShaders ||
(defined(lightColor) && !defined(value)) ||
(defined(value) && !defined(lightColor));
this._lightColor = Cartesian3.clone(value, lightColor);
},
},
/**
* The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
* This is used when {@link Model#specularEnvironmentMaps} and {@link Model#sphericalHarmonicCoefficients} are not defined.
*
* @memberof Model.prototype
*
* @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
* @type {Number}
* @default 0.2
*/
luminanceAtZenith: {
get: function () {
return this._luminanceAtZenith;
},
set: function (value) {
var lum = this._luminanceAtZenith;
if (value === lum) {
return;
}
this._shouldRegenerateShaders =
this._shouldRegenerateShaders ||
(defined(lum) && !defined(value)) ||
(defined(value) && !defined(lum));
this._luminanceAtZenith = value;
},
},
/**
* The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When <code>undefined</code>, a diffuse irradiance
* computed from the atmosphere color is used.
* <p>
* There are nine <code>Cartesian3</code> coefficients.
* The order of the coefficients is: L<sub>00</sub>, L<sub>1-1</sub>, L<sub>10</sub>, L<sub>11</sub>, L<sub>2-2</sub>, L<sub>2-1</sub>, L<sub>20</sub>, L<sub>21</sub>, L<sub>22</sub>
* </p>
*
* These values can be obtained by preprocessing the environment map using the <code>cmgen</code> tool of
* {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be
* supplied to {@link Model#specularEnvironmentMaps}.
*
* @memberof Model.prototype
*
* @type {Cartesian3[]}
* @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
* @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps}
*/
sphericalHarmonicCoefficients: {
get: function () {
return this._sphericalHarmonicCoefficients;
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
if (defined(value) && (!Array.isArray(value) || value.length !== 9)) {
throw new DeveloperError(
"sphericalHarmonicCoefficients must be an array of 9 Cartesian3 values."
);
}
//>>includeEnd('debug');
if (value === this._sphericalHarmonicCoefficients) {
return;
}
this._sphericalHarmonicCoefficients = value;
this._shouldRegenerateShaders = true;
},
},
/**
* A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
*
* @memberof Model.prototype
* @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
* @type {String}
* @see Model#sphericalHarmonicCoefficients
*/
specularEnvironmentMaps: {
get: function () {
return this._specularEnvironmentMaps;
},
set: function (value) {
this._shouldUpdateSpecularMapAtlas =
this._shouldUpdateSpecularMapAtlas ||
value !== this._specularEnvironmentMaps;
this._specularEnvironmentMaps = value;
},
},
/**
* Gets the credit that will be displayed for the model
* @memberof Model.prototype
* @type {Credit}
*/
credit: {
get: function () {
return this._credit;
},
},
});
function silhouetteSupported(context) {
return context.stencilBuffer;
}
function isColorShadingEnabled(model) {
return (
!Color.equals(model.color, Color.WHITE) ||
model.colorBlendMode !== ColorBlendMode.HIGHLIGHT
);
}
function isClippingEnabled(model) {
var clippingPlanes = model._clippingPlanes;
return (
defined(clippingPlanes) &&
clippingPlanes.enabled &&
clippingPlanes.length !== 0
);
}
/**
* Determines if silhouettes are supported.
*
* @param {Scene} scene The scene.
* @returns {Boolean} <code>true</code> if silhouettes are supported; otherwise, returns <code>false</code>
*/
Model.silhouetteSupported = function (scene) {
return silhouetteSupported(scene.context);
};
function containsGltfMagic(uint8Array) {
var magic = getMagic(uint8Array);
return magic === "glTF";
}
/**
* <p>
* Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
* and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved.
* </p>
* <p>
* The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the .glb extension.
* </p>
* <p>
* Cesium supports glTF assets with the following extensions:
* <ul>
* <li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl}
* </li><li>
* {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform}
* </li>
* </ul>
* </p>
* <p>
* For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the
* CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
* relative to a local origin.
* </p>
*
* @param {Object} options Object with the following properties:
* @param {Resource|String} options.url The url to the .gltf file.
* @param {Resource|String} [options.basePath] The base path that paths in the glTF JSON are relative to.
* @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
* @param {Number} [options.scale=1.0] A uniform scale applied to this model.
* @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
* @param {Number} [options.maximumScale] The maximum scale for the model.
* @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
* @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
* @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
* @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
* @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
* @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain.
* @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
* @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
* @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color.
* @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
* @param {Numbe