UNPKG

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
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