UNPKG

cesium

Version:

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

1,006 lines (903 loc) 42.2 kB
define([ '../Core/ColorGeometryInstanceAttribute', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/destroyObject', '../Core/DeveloperError', '../Core/GeometryInstance', '../Core/isArray', '../Renderer/DrawCommand', '../Renderer/Pass', '../Renderer/RenderState', '../Renderer/ShaderProgram', '../Renderer/ShaderSource', '../Shaders/ShadowVolumeFS', '../Shaders/ShadowVolumeVS', '../ThirdParty/when', './BlendingState', './ClassificationType', './DepthFunction', './PerInstanceColorAppearance', './Primitive', './SceneMode', './StencilFunction', './StencilOperation' ], function( ColorGeometryInstanceAttribute, defaultValue, defined, defineProperties, destroyObject, DeveloperError, GeometryInstance, isArray, DrawCommand, Pass, RenderState, ShaderProgram, ShaderSource, ShadowVolumeFS, ShadowVolumeVS, when, BlendingState, ClassificationType, DepthFunction, PerInstanceColorAppearance, Primitive, SceneMode, StencilFunction, StencilOperation) { 'use strict'; var ClassificationPrimitiveReadOnlyInstanceAttributes = ['color']; /** * A classification primitive represents a volume enclosing geometry in the {@link Scene} to be highlighted. The geometry must be from a single {@link GeometryInstance}. * Batching multiple geometries is not yet supported. * <p> * A primitive combines the geometry instance with an {@link Appearance} that describes the full shading, including * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement, * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix * and match most of them and add a new geometry or appearance independently of each other. Only the {@link PerInstanceColorAppearance} * is supported at this time. * </p> * <p> * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there * will be rendering artifacts for some viewing angles. * </p> * <p> * Valid geometries are {@link BoxGeometry}, {@link CylinderGeometry}, {@link EllipsoidGeometry}, {@link PolylineVolumeGeometry}, and {@link SphereGeometry}. * </p> * <p> * Geometries that follow the surface of the ellipsoid, such as {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}, * are also valid if they are extruded volumes; otherwise, they will not be rendered. * </p> * * @alias ClassificationPrimitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. This can either be a single instance or an array of length one. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory. * @param {Boolean} [options.releaseGeometryInstances=true] When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be <code>true</code> on * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be <code>false</code>. * * @see Primitive * @see GroundPrimitive * @see GeometryInstance * @see Appearance */ function ClassificationPrimitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The geometry instance rendered with this primitive. This may * be <code>undefined</code> if <code>options.releaseGeometryInstances</code> * is <code>true</code> when the primitive is constructed. * <p> * Changing this property after the primitive is rendered has no effect. * </p> * <p> * Because of the rendering technique used, all geometry instances must be the same color. * If there is an instance with a differing color, a <code>DeveloperError</code> will be thrown * on the first attempt to render. * </p> * * @readonly * @type {Array|GeometryInstance} * * @default undefined */ this.geometryInstances = options.geometryInstances; /** * Determines if the primitive will be shown. This affects all geometry * instances in the primitive. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * Determines whether terrain, 3D Tiles or both will be classified. * * @type {ClassificationType} * * @default ClassificationType.BOTH */ this.classificationType = defaultValue(options.classificationType, ClassificationType.BOTH); /** * 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 primitive. * </p> * * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false); /** * This property is for debugging only; it is not for production use nor is it optimized. * <p> * Draws the shadow volume for each geometry in the primitive. * </p> * * @type {Boolean} * * @default false */ this.debugShowShadowVolume = defaultValue(options.debugShowShadowVolume, false); this._debugShowShadowVolume = false; // These are used by GroundPrimitive to augment the shader and uniform map. this._extruded = defaultValue(options._extruded, false); this._uniformMap = options._uniformMap; this._sp = undefined; this._spStencil = undefined; this._spPick = undefined; this._rsStencilPreloadPass = undefined; this._rsStencilDepthPass = undefined; this._rsColorPass = undefined; this._rsPickPass = undefined; this._commandsIgnoreShow = []; this._ready = false; this._readyPromise = when.defer(); this._primitive = undefined; this._pickPrimitive = options._pickPrimitive; var appearance = new PerInstanceColorAppearance({ flat : true }); var readOnlyAttributes; if (defined(this.geometryInstances) && isArray(this.geometryInstances) && this.geometryInstances.length > 1) { readOnlyAttributes = ClassificationPrimitiveReadOnlyInstanceAttributes; } this._createBoundingVolumeFunction = options._createBoundingVolumeFunction; this._updateAndQueueCommandsFunction = options._updateAndQueueCommandsFunction; this._primitiveOptions = { geometryInstances : undefined, appearance : appearance, vertexCacheOptimize : defaultValue(options.vertexCacheOptimize, false), interleave : defaultValue(options.interleave, false), releaseGeometryInstances : defaultValue(options.releaseGeometryInstances, true), allowPicking : defaultValue(options.allowPicking, true), asynchronous : defaultValue(options.asynchronous, true), compressVertices : defaultValue(options.compressVertices, true), _readOnlyInstanceAttributes : readOnlyAttributes, _createBoundingVolumeFunction : undefined, _createRenderStatesFunction : undefined, _createShaderProgramFunction : undefined, _createCommandsFunction : undefined, _updateAndQueueCommandsFunction : undefined, _createPickOffsets : true }; } defineProperties(ClassificationPrimitive.prototype, { /** * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ vertexCacheOptimize : { get : function() { return this._primitiveOptions.vertexCacheOptimize; } }, /** * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default false */ interleave : { get : function() { return this._primitiveOptions.interleave; } }, /** * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ releaseGeometryInstances : { get : function() { return this._primitiveOptions.releaseGeometryInstances; } }, /** * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking : { get : function() { return this._primitiveOptions.allowPicking; } }, /** * Determines if the geometry instances will be created and batched on a web worker. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous : { get : function() { return this._primitiveOptions.asynchronous; } }, /** * When <code>true</code>, geometry vertices are compressed, which will save memory. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ compressVertices : { get : function() { return this._primitiveOptions.compressVertices; } }, /** * Determines if the primitive is complete and ready to render. If this property is * true, the primitive will be rendered the next time that {@link ClassificationPrimitive#update} * is called. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly */ ready : { get : function() { return this._ready; } }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof ClassificationPrimitive.prototype * @type {Promise.<ClassificationPrimitive>} * @readonly */ readyPromise : { get : function() { return this._readyPromise.promise; } } }); /** * Determines if ClassificationPrimitive rendering is supported. * * @param {Scene} scene The scene. * @returns {Boolean} <code>true</code> if ClassificationPrimitives are supported; otherwise, returns <code>false</code> */ ClassificationPrimitive.isSupported = function(scene) { return scene.context.stencilBuffer; }; // The stencil mask only uses the least significant 4 bits. // This is so 3D Tiles with the skip LOD optimization, which uses the most significant 4 bits, // can be classified. var stencilMask = 0x0F; var stencilReference = 0; function getStencilPreloadRenderState(enableStencil) { return { colorMask : { red : false, green : false, blue : false, alpha : false }, stencilTest : { enabled : enableStencil, frontFunction : StencilFunction.ALWAYS, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.DECREMENT_WRAP, zPass : StencilOperation.DECREMENT_WRAP }, backFunction : StencilFunction.ALWAYS, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.INCREMENT_WRAP, zPass : StencilOperation.INCREMENT_WRAP }, reference : stencilReference, mask : stencilMask }, depthTest : { enabled : false }, depthMask : false }; } function getStencilDepthRenderState(enableStencil) { return { colorMask : { red : false, green : false, blue : false, alpha : false }, stencilTest : { enabled : enableStencil, frontFunction : StencilFunction.ALWAYS, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.INCREMENT_WRAP }, backFunction : StencilFunction.ALWAYS, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, reference : stencilReference, mask : stencilMask }, depthTest : { enabled : true, func : DepthFunction.LESS_OR_EQUAL }, depthMask : false }; } function getColorRenderState(enableStencil) { return { stencilTest : { enabled : enableStencil, frontFunction : StencilFunction.NOT_EQUAL, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, backFunction : StencilFunction.NOT_EQUAL, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, reference : stencilReference, mask : stencilMask }, depthTest : { enabled : false }, depthMask : false, blending : BlendingState.ALPHA_BLEND }; } var pickRenderState = { stencilTest : { enabled : true, frontFunction : StencilFunction.NOT_EQUAL, frontOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, backFunction : StencilFunction.NOT_EQUAL, backOperation : { fail : StencilOperation.KEEP, zFail : StencilOperation.KEEP, zPass : StencilOperation.DECREMENT_WRAP }, reference : stencilReference, mask : stencilMask }, depthTest : { enabled : false }, depthMask : false }; function createRenderStates(classificationPrimitive, context, appearance, twoPasses) { if (defined(classificationPrimitive._rsStencilPreloadPass)) { return; } var stencilEnabled = !classificationPrimitive.debugShowShadowVolume; classificationPrimitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(stencilEnabled)); classificationPrimitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(stencilEnabled)); classificationPrimitive._rsColorPass = RenderState.fromCache(getColorRenderState(stencilEnabled)); classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState); } function modifyForEncodedNormals(primitive, vertexShaderSource) { if (!primitive.compressVertices) { return vertexShaderSource; } if (vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1) { var attributeName = 'compressedAttributes'; //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes var attributeDecl = 'attribute vec2 ' + attributeName + ';'; var globalDecl = 'vec3 extrudeDirection;\n'; var decode = ' extrudeDirection = czm_octDecode(' + attributeName + ', 65535.0);\n'; var modifiedVS = vertexShaderSource; modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+extrudeDirection;/g, ''); modifiedVS = ShaderSource.replaceMain(modifiedVS, 'czm_non_compressed_main'); var compressedMain = 'void main() \n' + '{ \n' + decode + ' czm_non_compressed_main(); \n' + '}'; return [attributeDecl, globalDecl, modifiedVS, compressedMain].join('\n'); } } function createShaderProgram(classificationPrimitive, frameState, appearance) { if (defined(classificationPrimitive._sp)) { return; } var context = frameState.context; var primitive = classificationPrimitive._primitive; var vs = ShadowVolumeVS; vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()(vs); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs); vs = Primitive._modifyShaderPosition(classificationPrimitive, vs, frameState.scene3DOnly); vs = Primitive._updateColorAttribute(primitive, vs); if (classificationPrimitive._extruded) { vs = modifyForEncodedNormals(primitive, vs); } var extrudedDefine = classificationPrimitive._extruded ? 'EXTRUDED_GEOMETRY' : ''; // Tesselation on ClassificationPrimitives tends to be low, // which causes problems when interpolating log depth from vertices. // So force computing and writing logarithmic depth in the fragment shader. // Re-enable at far distances to avoid z-fighting. var disableGlPositionLogDepth = 'ENABLE_GL_POSITION_LOG_DEPTH_AT_HEIGHT'; var vsSource = new ShaderSource({ defines : [extrudedDefine, disableGlPositionLogDepth], sources : [vs] }); var fsSource = new ShaderSource({ sources : [ShadowVolumeFS] }); var attributeLocations = classificationPrimitive._primitive._attributeLocations; classificationPrimitive._spStencil = ShaderProgram.replaceCache({ context : context, shaderProgram : classificationPrimitive._spStencil, vertexShaderSource : vsSource, fragmentShaderSource : fsSource, attributeLocations : attributeLocations }); if (classificationPrimitive._primitive.allowPicking) { var vsPick = ShaderSource.createPickVertexShaderSource(vs); vsPick = Primitive._appendShowToShader(primitive, vsPick); vsPick = Primitive._updatePickColorAttribute(vsPick); var pickVS = new ShaderSource({ defines : [extrudedDefine, disableGlPositionLogDepth], sources : [vsPick] }); var pickFS = new ShaderSource({ sources : [ShadowVolumeFS], pickColorQualifier : 'varying' }); classificationPrimitive._spPick = ShaderProgram.replaceCache({ context : context, shaderProgram : classificationPrimitive._spPick, vertexShaderSource : pickVS, fragmentShaderSource : pickFS, attributeLocations : attributeLocations }); } else { classificationPrimitive._spPick = ShaderProgram.fromCache({ context : context, vertexShaderSource : vsSource, fragmentShaderSource : fsSource, attributeLocations : attributeLocations }); } vs = Primitive._appendShowToShader(primitive, vs); vsSource = new ShaderSource({ defines : [extrudedDefine, disableGlPositionLogDepth], sources : [vs] }); classificationPrimitive._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : classificationPrimitive._sp, vertexShaderSource : vsSource, fragmentShaderSource : fsSource, attributeLocations : attributeLocations }); } function createColorCommands(classificationPrimitive, colorCommands) { var primitive = classificationPrimitive._primitive; var length = primitive._va.length * 3; colorCommands.length = length; var i; var command; var vaIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap); for (i = 0; i < length; i += 3) { var vertexArray = primitive._va[vaIndex++]; // stencil preload command command = colorCommands[i]; if (!defined(command)) { command = colorCommands[i] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsStencilPreloadPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; // stencil depth command command = colorCommands[i + 1]; if (!defined(command)) { command = colorCommands[i + 1] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; // color command command = colorCommands[i + 2]; if (!defined(command)) { command = colorCommands[i + 2] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsColorPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; } var commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow; var spStencil = classificationPrimitive._spStencil; var commandIndex = 0; length = commandsIgnoreShow.length = length / 3 * 2; for (var j = 0; j < length; j += 2) { var commandIgnoreShow = commandsIgnoreShow[j] = DrawCommand.shallowClone(colorCommands[commandIndex], commandsIgnoreShow[j]); commandIgnoreShow.shaderProgram = spStencil; commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; commandIgnoreShow = commandsIgnoreShow[j + 1] = DrawCommand.shallowClone(colorCommands[commandIndex + 1], commandsIgnoreShow[j + 1]); commandIgnoreShow.shaderProgram = spStencil; commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; commandIndex += 3; } } function createPickCommands(classificationPrimitive, pickCommands) { var primitive = classificationPrimitive._primitive; var pickOffsets = primitive._pickOffsets; var length = pickOffsets.length * 3; pickCommands.length = length; var j; var command; var pickIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()(classificationPrimitive._uniformMap); for (j = 0; j < length; j += 3) { var pickOffset = pickOffsets[pickIndex++]; var offset = pickOffset.offset; var count = pickOffset.count; var vertexArray = primitive._va[pickOffset.index]; // stencil preload command command = pickCommands[j]; if (!defined(command)) { command = pickCommands[j] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.offset = offset; command.count = count; command.renderState = classificationPrimitive._rsStencilPreloadPass; command.shaderProgram = classificationPrimitive._spStencil; command.uniformMap = uniformMap; // stencil depth command command = pickCommands[j + 1]; if (!defined(command)) { command = pickCommands[j + 1] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.offset = offset; command.count = count; command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._spStencil; command.uniformMap = uniformMap; // color command command = pickCommands[j + 2]; if (!defined(command)) { command = pickCommands[j + 2] = new DrawCommand({ owner : classificationPrimitive, primitiveType : primitive._primitiveType }); } command.vertexArray = vertexArray; command.offset = offset; command.count = count; command.renderState = classificationPrimitive._rsPickPass; command.shaderProgram = classificationPrimitive._spPick; command.uniformMap = uniformMap; } } function createCommands(classificationPrimitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) { createColorCommands(classificationPrimitive, colorCommands); createPickCommands(classificationPrimitive, pickCommands); } function boundingVolumeIndex(commandIndex, length) { return Math.floor((commandIndex % length) / 3); } function updateAndQueueCommands(classificationPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { var primitive = classificationPrimitive._primitive; Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); var boundingVolumes; if (frameState.mode === SceneMode.SCENE3D) { boundingVolumes = primitive._boundingSphereWC; } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) { boundingVolumes = primitive._boundingSphereCV; } else if (frameState.mode === SceneMode.SCENE2D && defined(primitive._boundingSphere2D)) { boundingVolumes = primitive._boundingSphere2D; } else if (defined(primitive._boundingSphereMorph)) { boundingVolumes = primitive._boundingSphereMorph; } var commandList = frameState.commandList; var passes = frameState.passes; var i; var pass; switch (classificationPrimitive.classificationType) { case ClassificationType.TERRAIN: pass = Pass.TERRAIN_CLASSIFICATION; break; case ClassificationType.CESIUM_3D_TILE: pass = Pass.CESIUM_3D_TILE_CLASSIFICATION; break; default: pass = Pass.CLASSIFICATION; } if (passes.render) { var colorCommand; var colorLength = colorCommands.length; for (i = 0; i < colorLength; ++i) { colorCommand = colorCommands[i]; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; colorCommand.cull = cull; colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; colorCommand.pass = pass; commandList.push(colorCommand); } if (frameState.invertClassification) { var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; var ignoreShowCommandsLength = ignoreShowCommands.length; for (i = 0; i < ignoreShowCommandsLength; ++i) { var bvIndex = Math.floor(i / 2); colorCommand = ignoreShowCommands[i]; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingVolumes[bvIndex]; colorCommand.cull = cull; colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; commandList.push(colorCommand); } } } if (passes.pick) { var pickLength = pickCommands.length; var pickOffsets = primitive._pickOffsets; for (i = 0; i < pickLength; ++i) { var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)]; var pickCommand = pickCommands[i]; pickCommand.modelMatrix = modelMatrix; pickCommand.boundingVolume = boundingVolumes[pickOffset.index]; pickCommand.cull = cull; pickCommand.pass = pass; commandList.push(pickCommand); } } } /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. * <p> * Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: * </p> * * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. * @exception {DeveloperError} Not all of the geometry instances have the same color attribute. */ ClassificationPrimitive.prototype.update = function(frameState) { if (!defined(this._primitive) && !defined(this.geometryInstances)) { return; } var that = this; var primitiveOptions = this._primitiveOptions; if (!defined(this._primitive)) { var instances = isArray(this.geometryInstances) ? this.geometryInstances : [this.geometryInstances]; var length = instances.length; var i; var instance; //>>includeStart('debug', pragmas.debug); var color; for (i = 0; i < length; ++i) { instance = instances[i]; var attributes = instance.attributes; if (!defined(attributes) || !defined(attributes.color)) { throw new DeveloperError('Not all of the geometry instances have the same color attribute.'); } else if (defined(color) && !ColorGeometryInstanceAttribute.equals(color, attributes.color)) { throw new DeveloperError('Not all of the geometry instances have the same color attribute.'); } else if (!defined(color)) { color = attributes.color; } } //>>includeEnd('debug'); var geometryInstances = new Array(length); for (i = 0; i < length; ++i) { instance = instances[i]; geometryInstances[i] = new GeometryInstance({ geometry : instance.geometry, attributes : instance.attributes, modelMatrix : instance.modelMatrix, id : instance.id, pickPrimitive : defaultValue(this._pickPrimitive, that) }); } primitiveOptions.geometryInstances = geometryInstances; if (defined(this._createBoundingVolumeFunction)) { primitiveOptions._createBoundingVolumeFunction = function(frameState, geometry) { that._createBoundingVolumeFunction(frameState, geometry); }; } primitiveOptions._createRenderStatesFunction = function(primitive, context, appearance, twoPasses) { createRenderStates(that, context); }; primitiveOptions._createShaderProgramFunction = function(primitive, frameState, appearance) { createShaderProgram(that, frameState); }; primitiveOptions._createCommandsFunction = function(primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands) { createCommands(that, undefined, undefined, true, false, colorCommands, pickCommands); }; if (defined(this._updateAndQueueCommandsFunction)) { primitiveOptions._updateAndQueueCommandsFunction = function(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { that._updateAndQueueCommandsFunction(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses); }; } else { primitiveOptions._updateAndQueueCommandsFunction = function(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { updateAndQueueCommands(that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses); }; } this._primitive = new Primitive(primitiveOptions); this._primitive.readyPromise.then(function(primitive) { that._ready = true; if (that.releaseGeometryInstances) { that.geometryInstances = undefined; } var error = primitive._error; if (!defined(error)) { that._readyPromise.resolve(that); } else { that._readyPromise.reject(error); } }); } if (this.debugShowShadowVolume && !this._debugShowShadowVolume && this._ready) { this._debugShowShadowVolume = true; this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false)); this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false)); this._rsColorPass = RenderState.fromCache(getColorRenderState(false)); } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) { this._debugShowShadowVolume = false; this._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(true)); this._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(true)); this._rsColorPass = RenderState.fromCache(getColorRenderState(true)); } this._primitive.show = this.show; this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; this._primitive.update(frameState); }; /** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {Object} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); */ ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function(id) { //>>includeStart('debug', pragmas.debug); if (!defined(this._primitive)) { throw new DeveloperError('must call update before calling getGeometryInstanceAttributes'); } //>>includeEnd('debug'); return this._primitive.getGeometryInstanceAttributes(id); }; /** * Returns true if this object was destroyed; otherwise, false. * <p> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * </p> * * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see ClassificationPrimitive#destroy */ ClassificationPrimitive.prototype.isDestroyed = function() { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <p> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * </p> * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * e = e && e.destroy(); * * @see ClassificationPrimitive#isDestroyed */ ClassificationPrimitive.prototype.destroy = function() { this._primitive = this._primitive && this._primitive.destroy(); this._sp = this._sp && this._sp.destroy(); this._spPick = this._spPick && this._spPick.destroy(); return destroyObject(this); }; return ClassificationPrimitive; });