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