cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
536 lines (463 loc) • 20.2 kB
JavaScript
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 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;
}
Object.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.
* <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>.
*/
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.
* <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.
*/
Vector3DTilePolylines.prototype.destroy = function() {
this._va = this._va && this._va.destroy();
this._sp = this._sp && this._sp.destroy();
return destroyObject(this);
};
export default Vector3DTilePolylines;