import arraySlice from '../Core/arraySlice.js'; import Cartesian3 from '../Core/Cartesian3.js'; import Color from '../Core/Color.js'; import ComponentDatatype from '../Core/ComponentDatatype.js'; import defaultValue from '../Core/defaultValue.js'; import defined from '../Core/defined.js'; import defineProperties from '../Core/defineProperties.js'; import destroyObject from '../Core/destroyObject.js'; import Ellipsoid from '../Core/Ellipsoid.js'; import FeatureDetection from '../Core/FeatureDetection.js'; import IndexDatatype from '../Core/IndexDatatype.js'; import Matrix4 from '../Core/Matrix4.js'; import Rectangle from '../Core/Rectangle.js'; import TaskProcessor from '../Core/TaskProcessor.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 ShaderProgram from '../Renderer/ShaderProgram.js'; import ShaderSource from '../Renderer/ShaderSource.js'; import VertexArray from '../Renderer/VertexArray.js'; import PolylineCommon from '../Shaders/PolylineCommon.js'; import Vector3DTilePolylinesVS from '../Shaders/Vector3DTilePolylinesVS.js'; import when from '../ThirdParty/when.js'; import BlendingState from './BlendingState.js'; import Cesium3DTileFeature from './Cesium3DTileFeature.js'; /** * Creates a batch of polylines that have been subdivided to be draped on terrain. * * @alias Vector3DTilePolylines * @constructor * * @param {Object} options An object with following properties: * @param {Uint16Array} options.positions The positions of the polylines * @param {Uint32Array} options.counts The number or positions in the each polyline. * @param {Uint16Array} options.widths The width of each polyline. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Rectangle} options.rectangle The rectangle containing the tile. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polylines. * @param {Uint16Array} options.batchIds The batch ids for each polyline. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of polylines. * * @private */ function Vector3DTilePolylines(options) { // these arrays are all released after the first update. this._positions = options.positions; this._widths = options.widths; this._counts = options.counts; this._batchIds = options.batchIds; this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._minimumHeight = options.minimumHeight; this._maximumHeight = options.maximumHeight; this._center = options.center; this._rectangle = options.rectangle; this._boundingVolume = options.boundingVolume; this._batchTable = options.batchTable; this._va = undefined; this._sp = undefined; this._rs = undefined; this._uniformMap = undefined; this._command = undefined; this._transferrableBatchIds = undefined; this._packedBuffer = undefined; this._currentPositions = undefined; this._previousPositions = undefined; this._nextPositions = undefined; this._expandAndWidth = undefined; this._vertexBatchIds = undefined; this._indices = undefined; this._constantColor = Color.clone(Color.WHITE); this._highlightColor = this._constantColor; this._trianglesLength = 0; this._geometryByteLength = 0; this._ready = false; this._readyPromise = when.defer(); this._verticesPromise = undefined; } defineProperties(Vector3DTilePolylines.prototype, { /** * Gets the number of triangles. * * @memberof Vector3DTilePolylines.prototype * * @type {Number} * @readonly */ trianglesLength : { get : function() { return this._trianglesLength; } }, /** * Gets the geometry memory in bytes. * * @memberof Vector3DTilePolylines.prototype * * @type {Number} * @readonly */ geometryByteLength : { get : function() { return this._geometryByteLength; } }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTilePolylines.prototype * @type {Promise} * @readonly */ readyPromise : { get : function() { return this._readyPromise.promise; } } }); function packBuffer(polylines) { var rectangle = polylines._rectangle; var minimumHeight = polylines._minimumHeight; var maximumHeight = polylines._maximumHeight; var ellipsoid = polylines._ellipsoid; var center = polylines._center; var packedLength = 2 + Rectangle.packedLength + Ellipsoid.packedLength + Cartesian3.packedLength; var packedBuffer = new Float64Array(packedLength); var offset = 0; packedBuffer[offset++] = minimumHeight; packedBuffer[offset++] = maximumHeight; Rectangle.pack(rectangle, packedBuffer, offset); offset += Rectangle.packedLength; Ellipsoid.pack(ellipsoid, packedBuffer, offset); offset += Ellipsoid.packedLength; Cartesian3.pack(center, packedBuffer, offset); return packedBuffer; } var createVerticesTaskProcessor = new TaskProcessor('createVectorTilePolylines'); var attributeLocations = { previousPosition : 0, currentPosition : 1, nextPosition : 2, expandAndWidth : 3, a_batchId : 4 }; function createVertexArray(polylines, context) { if (defined(polylines._va)) { return; } if (!defined(polylines._verticesPromise)) { var positions = polylines._positions; var widths = polylines._widths; var counts = polylines._counts; var batchIds = polylines._transferrableBatchIds; var packedBuffer = polylines._packedBuffer; if (!defined(packedBuffer)) { // Copy because they may be the views on the same buffer. positions = polylines._positions = arraySlice(positions); widths = polylines._widths = arraySlice(widths); counts = polylines._counts = arraySlice(counts); batchIds = polylines._transferrableBatchIds = arraySlice(polylines._batchIds); packedBuffer = polylines._packedBuffer = packBuffer(polylines); } var transferrableObjects = [positions.buffer, widths.buffer, counts.buffer, batchIds.buffer, packedBuffer.buffer]; var parameters = { positions : positions.buffer, widths : widths.buffer, counts : counts.buffer, batchIds : batchIds.buffer, packedBuffer : packedBuffer.buffer }; var verticesPromise = polylines._verticesPromise = createVerticesTaskProcessor.scheduleTask(parameters, transferrableObjects); if (!defined(verticesPromise)) { // Postponed return; } when(verticesPromise, function(result) { polylines._currentPositions = new Float32Array(result.currentPositions); polylines._previousPositions = new Float32Array(result.previousPositions); polylines._nextPositions = new Float32Array(result.nextPositions); polylines._expandAndWidth = new Float32Array(result.expandAndWidth); polylines._vertexBatchIds = new Uint16Array(result.batchIds); var indexDatatype = result.indexDatatype; polylines._indices = indexDatatype === IndexDatatype.UNSIGNED_SHORT ? new Uint16Array(result.indices) : new Uint32Array(result.indices); polylines._ready = true; }); } if (polylines._ready && !defined(polylines._va)) { var curPositions = polylines._currentPositions; var prevPositions = polylines._previousPositions; var nextPositions = polylines._nextPositions; var expandAndWidth = polylines._expandAndWidth; var vertexBatchIds = polylines._vertexBatchIds; var indices = polylines._indices; var byteLength = prevPositions.byteLength + curPositions.byteLength + nextPositions.byteLength; byteLength += expandAndWidth.byteLength + vertexBatchIds.byteLength + indices.byteLength; polylines._trianglesLength = indices.length / 3; polylines._geometryByteLength = byteLength; var prevPositionBuffer = Buffer.createVertexBuffer({ context : context, typedArray : prevPositions, usage : BufferUsage.STATIC_DRAW }); var curPositionBuffer = Buffer.createVertexBuffer({ context : context, typedArray : curPositions, usage : BufferUsage.STATIC_DRAW }); var nextPositionBuffer = Buffer.createVertexBuffer({ context : context, typedArray : nextPositions, usage : BufferUsage.STATIC_DRAW }); var expandAndWidthBuffer = Buffer.createVertexBuffer({ context : context, typedArray : expandAndWidth, usage : BufferUsage.STATIC_DRAW }); var idBuffer = Buffer.createVertexBuffer({ context : context, typedArray : vertexBatchIds, usage : BufferUsage.STATIC_DRAW }); var indexBuffer = Buffer.createIndexBuffer({ context : context, typedArray : indices, usage : BufferUsage.STATIC_DRAW, indexDatatype : (indices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT }); var vertexAttributes = [{ index : attributeLocations.previousPosition, vertexBuffer : prevPositionBuffer, componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 }, { index : attributeLocations.currentPosition, vertexBuffer : curPositionBuffer, componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 }, { index : attributeLocations.nextPosition, vertexBuffer : nextPositionBuffer, componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 }, { index : attributeLocations.expandAndWidth, vertexBuffer : expandAndWidthBuffer, componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 2 }, { index : attributeLocations.a_batchId, vertexBuffer : idBuffer, componentDatatype : ComponentDatatype.UNSIGNED_SHORT, componentsPerAttribute : 1 }]; polylines._va = new VertexArray({ context : context, attributes : vertexAttributes, indexBuffer : indexBuffer }); polylines._positions = undefined; polylines._widths = undefined; polylines._counts = undefined; polylines._ellipsoid = undefined; polylines._minimumHeight = undefined; polylines._maximumHeight = undefined; polylines._rectangle = undefined; polylines._transferrableBatchIds = undefined; polylines._packedBuffer = undefined; polylines._currentPositions = undefined; polylines._previousPositions = undefined; polylines._nextPositions = undefined; polylines._expandAndWidth = undefined; polylines._vertexBatchIds = undefined; polylines._indices = undefined; polylines._readyPromise.resolve(); } } var modifiedModelViewScratch = new Matrix4(); var rtcScratch = new Cartesian3(); function createUniformMap(primitive, context) { if (defined(primitive._uniformMap)) { return; } primitive._uniformMap = { u_modifiedModelView : function() { var viewMatrix = context.uniformState.view; Matrix4.clone(viewMatrix, modifiedModelViewScratch); Matrix4.multiplyByPoint(modifiedModelViewScratch, primitive._center, rtcScratch); Matrix4.setTranslation(modifiedModelViewScratch, rtcScratch, modifiedModelViewScratch); return modifiedModelViewScratch; }, u_highlightColor : function() { return primitive._highlightColor; } }; } function createRenderStates(primitive) { if (defined(primitive._rs)) { return; } var polygonOffset = { enabled : true, factor : -5.0, units : -5.0 }; primitive._rs = RenderState.fromCache({ blending : BlendingState.ALPHA_BLEND, depthMask : false, depthTest : { enabled : true }, polygonOffset : polygonOffset }); } var PolylineFS = 'uniform vec4 u_highlightColor; \n' + 'void main()\n' + '{\n' + ' gl_FragColor = u_highlightColor;\n' + '}\n'; function createShaders(primitive, context) { if (defined(primitive._sp)) { return; } var batchTable = primitive._batchTable; var vsSource = batchTable.getVertexShaderCallback(false, 'a_batchId', undefined)(Vector3DTilePolylinesVS); var fsSource = batchTable.getFragmentShaderCallback()(PolylineFS, false, undefined); var vs = new ShaderSource({ defines : ['VECTOR_TILE', !FeatureDetection.isInternetExplorer() ? 'CLIP_POLYLINE' : ''], sources : [PolylineCommon, vsSource] }); var fs = new ShaderSource({ defines : ['VECTOR_TILE'], sources : [fsSource] }); primitive._sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations : attributeLocations }); } function queueCommands(primitive, frameState) { if (!defined(primitive._command)) { var uniformMap = primitive._batchTable.getUniformMapCallback()(primitive._uniformMap); primitive._command = new DrawCommand({ owner : primitive, vertexArray : primitive._va, renderState : primitive._rs, shaderProgram : primitive._sp, uniformMap : uniformMap, boundingVolume : primitive._boundingVolume, pass : Pass.TRANSLUCENT, pickId : primitive._batchTable.getPickId() }); } frameState.commandList.push(primitive._command); } /** * Creates features for each polyline and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed. */ Vector3DTilePolylines.prototype.createFeatures = function(content, features) { var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; features[batchId] = new Cesium3DTileFeature(content, batchId); } }; /** * Colors the entire tile when enabled is true. The resulting color will be (polyline batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePolylines.prototype.applyDebugSettings = function(enabled, color) { this._highlightColor = enabled ? color : this._constantColor; }; function clearStyle(polygons, features) { var batchIds = polygons._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.show = true; feature.color = Color.WHITE; } } var scratchColor = new Color(); var DEFAULT_COLOR_VALUE = Color.WHITE; var DEFAULT_SHOW_VALUE = true; /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePolylines.prototype.applyStyle = function(style, features) { if (!defined(style)) { clearStyle(this, features); return; } var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.color = defined(style.color) ? style.color.evaluateColor(feature, scratchColor) : DEFAULT_COLOR_VALUE; feature.show = defined(style.show) ? style.show.evaluate(feature) : DEFAULT_SHOW_VALUE; } }; /** * Updates the batches and queues the commands for rendering. * * @param {FrameState} frameState The current frame state. */ Vector3DTilePolylines.prototype.update = function(frameState) { var context = frameState.context; createVertexArray(this, context); createUniformMap(this, context); createShaders(this, context); createRenderStates(this); if (!this._ready) { return; } var passes = frameState.passes; if (passes.render || passes.pick) { queueCommands(this, frameState); } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTilePolylines.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. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTilePolylines.prototype.destroy = function() { this._va = this._va && this._va.destroy(); this._sp = this._sp && this._sp.destroy(); return destroyObject(this); }; export default Vector3DTilePolylines;