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 IndexDatatype from '../Core/IndexDatatype.js';
|
import Matrix4 from '../Core/Matrix4.js';
|
import PrimitiveType from '../Core/PrimitiveType.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 ShadowVolumeFS from '../Shaders/ShadowVolumeFS.js';
|
import VectorTileVS from '../Shaders/VectorTileVS.js';
|
import BlendingState from './BlendingState.js';
|
import Cesium3DTileFeature from './Cesium3DTileFeature.js';
|
import ClassificationType from './ClassificationType.js';
|
import DepthFunction from './DepthFunction.js';
|
import Expression from './Expression.js';
|
import StencilConstants from './StencilConstants.js';
|
import StencilFunction from './StencilFunction.js';
|
import StencilOperation from './StencilOperation.js';
|
import Vector3DTileBatch from './Vector3DTileBatch.js';
|
|
/**
|
* Creates a batch of classification meshes.
|
*
|
* @alias Vector3DTilePrimitive
|
* @constructor
|
*
|
* @param {Object} options An object with following properties:
|
* @param {Float32Array} options.positions The positions of the meshes.
|
* @param {Uint16Array|Uint32Array} options.indices The indices of the triangulated meshes. The indices must be contiguous so that
|
* the indices for mesh n are in [i, i + indexCounts[n]] where i = sum{indexCounts[0], indexCounts[n - 1]}.
|
* @param {Uint32Array} options.indexCounts The number of indices for each mesh.
|
* @param {Uint32Array} options.indexOffsets The offset into the index buffer for each mesh.
|
* @param {Vector3DTileBatch[]} options.batchedIndices The index offset and count for each batch with the same color.
|
* @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center.
|
* @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched meshes.
|
* @param {Uint16Array} options.batchIds The batch ids for each mesh.
|
* @param {Uint16Array} options.vertexBatchIds The batch id for each vertex.
|
* @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of meshes.
|
* @param {BoundingSphere[]} options.boundingVolumes The bounding volume for each mesh.
|
* @param {ClassificationType} [options.classificationType] What this tile will classify.
|
*
|
* @private
|
*/
|
function Vector3DTilePrimitive(options) {
|
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
|
|
this._batchTable = options.batchTable;
|
this._batchIds = options.batchIds;
|
|
// These arrays are released after VAO creation.
|
this._positions = options.positions;
|
this._vertexBatchIds = options.vertexBatchIds;
|
|
// These arrays are kept for re-batching indices based on colors.
|
// If WebGL 2 is supported, indices will be released and re-batching uses buffer-to-buffer copies.
|
this._indices = options.indices;
|
this._indexCounts = options.indexCounts;
|
this._indexOffsets = options.indexOffsets;
|
this._batchedIndices = options.batchedIndices;
|
|
this._boundingVolume = options.boundingVolume;
|
this._boundingVolumes = options.boundingVolumes;
|
|
this._center = defaultValue(options.center, Cartesian3.ZERO);
|
|
this._va = undefined;
|
this._sp = undefined;
|
this._spStencil = undefined;
|
this._spPick = undefined;
|
this._uniformMap = undefined;
|
|
// Only used with WebGL 2 to ping-pong ibos after copy.
|
this._vaSwap = undefined;
|
|
this._rsStencilPreloadPass = undefined;
|
this._rsStencilPreloadPass3DTiles = undefined;
|
this._rsStencilDepthPass = undefined;
|
this._rsStencilDepthPass3DTiles = undefined;
|
this._rsColorPass = undefined;
|
this._rsPickPass = undefined;
|
this._rsWireframe = undefined;
|
|
this._commands = [];
|
this._commandsIgnoreShow = [];
|
this._pickCommands = [];
|
|
this._constantColor = Color.clone(Color.WHITE);
|
this._highlightColor = this._constantColor;
|
|
this._batchDirty = true;
|
this._pickCommandsDirty = true;
|
this._framesSinceLastRebatch = 0;
|
|
this._updatingAllCommands = false;
|
|
this._trianglesLength = this._indices.length / 3;
|
this._geometryByteLength = this._indices.byteLength + this._positions.byteLength + this._vertexBatchIds.byteLength;
|
|
/**
|
* Draw the wireframe of the classification meshes.
|
* @type {Boolean}
|
* @default false
|
*/
|
this.debugWireframe = false;
|
this._debugWireframe = this.debugWireframe;
|
this._wireframeDirty = false;
|
|
/**
|
* Forces a re-batch instead of waiting after a number of frames have been rendered. For testing only.
|
* @type {Boolean}
|
* @default false
|
*/
|
this.forceRebatch = false;
|
|
/**
|
* What this tile will classify.
|
* @type {ClassificationType}
|
* @default ClassificationType.BOTH
|
*/
|
this.classificationType = defaultValue(options.classificationType, ClassificationType.BOTH);
|
|
// Hidden options
|
this._vertexShaderSource = options._vertexShaderSource;
|
this._fragmentShaderSource = options._fragmentShaderSource;
|
this._attributeLocations = options._attributeLocations;
|
this._uniformMap = options._uniformMap;
|
this._pickId = options._pickId;
|
this._modelMatrix = options._modelMatrix;
|
this._boundingSphere = options._boundingSphere;
|
|
this._batchIdLookUp = {};
|
|
var length = this._batchIds.length;
|
for (var i = 0; i < length; ++i) {
|
var batchId = this._batchIds[i];
|
this._batchIdLookUp[batchId] = i;
|
}
|
}
|
|
Object.defineProperties(Vector3DTilePrimitive.prototype, {
|
/**
|
* Gets the number of triangles.
|
*
|
* @memberof Vector3DTilePrimitive.prototype
|
*
|
* @type {Number}
|
* @readonly
|
*/
|
trianglesLength : {
|
get : function() {
|
return this._trianglesLength;
|
}
|
},
|
|
/**
|
* Gets the geometry memory in bytes.
|
*
|
* @memberof Vector3DTilePrimitive.prototype
|
*
|
* @type {Number}
|
* @readonly
|
*/
|
geometryByteLength : {
|
get : function() {
|
return this._geometryByteLength;
|
}
|
}
|
});
|
|
var defaultAttributeLocations = {
|
position : 0,
|
a_batchId : 1
|
};
|
|
function createVertexArray(primitive, context) {
|
if (defined(primitive._va)) {
|
return;
|
}
|
|
var positionBuffer = Buffer.createVertexBuffer({
|
context : context,
|
typedArray : primitive._positions,
|
usage : BufferUsage.STATIC_DRAW
|
});
|
var idBuffer = Buffer.createVertexBuffer({
|
context : context,
|
typedArray : primitive._vertexBatchIds,
|
usage : BufferUsage.STATIC_DRAW
|
});
|
var indexBuffer = Buffer.createIndexBuffer({
|
context : context,
|
typedArray : primitive._indices,
|
usage : BufferUsage.DYNAMIC_DRAW,
|
indexDatatype : (primitive._indices.BYTES_PER_ELEMENT === 2) ? IndexDatatype.UNSIGNED_SHORT : IndexDatatype.UNSIGNED_INT
|
});
|
|
var vertexAttributes = [{
|
index : 0,
|
vertexBuffer : positionBuffer,
|
componentDatatype : ComponentDatatype.fromTypedArray(primitive._positions),
|
componentsPerAttribute : 3
|
}, {
|
index : 1,
|
vertexBuffer : idBuffer,
|
componentDatatype : ComponentDatatype.fromTypedArray(primitive._vertexBatchIds),
|
componentsPerAttribute : 1
|
}];
|
|
primitive._va = new VertexArray({
|
context : context,
|
attributes : vertexAttributes,
|
indexBuffer : indexBuffer
|
});
|
|
if (context.webgl2) {
|
primitive._vaSwap = new VertexArray({
|
context : context,
|
attributes : vertexAttributes,
|
indexBuffer : Buffer.createIndexBuffer({
|
context : context,
|
sizeInBytes : indexBuffer.sizeInBytes,
|
usage : BufferUsage.DYNAMIC_DRAW,
|
indexDatatype : indexBuffer.indexDatatype
|
})
|
});
|
}
|
|
primitive._batchedPositions = undefined;
|
primitive._transferrableBatchIds = undefined;
|
primitive._vertexBatchIds = undefined;
|
primitive._verticesPromise = undefined;
|
}
|
|
function createShaders(primitive, context) {
|
if (defined(primitive._sp)) {
|
return;
|
}
|
|
var batchTable = primitive._batchTable;
|
var attributeLocations = defaultValue(primitive._attributeLocations, defaultAttributeLocations);
|
|
var pickId = primitive._pickId;
|
var vertexShaderSource = primitive._vertexShaderSource;
|
var fragmentShaderSource = primitive._fragmentShaderSource;
|
if (defined(vertexShaderSource)) {
|
primitive._sp = ShaderProgram.fromCache({
|
context : context,
|
vertexShaderSource : vertexShaderSource,
|
fragmentShaderSource : fragmentShaderSource,
|
attributeLocations : attributeLocations
|
});
|
primitive._spStencil = primitive._sp;
|
|
fragmentShaderSource = ShaderSource.replaceMain(fragmentShaderSource, 'czm_non_pick_main');
|
fragmentShaderSource =
|
fragmentShaderSource +
|
'void main() \n' +
|
'{ \n' +
|
' czm_non_pick_main(); \n' +
|
' gl_FragColor = ' + pickId + '; \n' +
|
'} \n';
|
primitive._spPick = ShaderProgram.fromCache({
|
context : context,
|
vertexShaderSource : vertexShaderSource,
|
fragmentShaderSource : fragmentShaderSource,
|
attributeLocations : attributeLocations
|
});
|
return;
|
}
|
|
var vsSource = batchTable.getVertexShaderCallback(false, 'a_batchId', undefined)(VectorTileVS);
|
var fsSource = batchTable.getFragmentShaderCallback()(ShadowVolumeFS, false, undefined);
|
|
pickId = batchTable.getPickId();
|
|
var vs = new ShaderSource({
|
sources : [vsSource]
|
});
|
var fs = new ShaderSource({
|
defines : ['VECTOR_TILE'],
|
sources : [fsSource]
|
});
|
|
primitive._sp = ShaderProgram.fromCache({
|
context : context,
|
vertexShaderSource : vs,
|
fragmentShaderSource : fs,
|
attributeLocations : attributeLocations
|
});
|
|
vs = new ShaderSource({
|
sources : [VectorTileVS]
|
});
|
fs = new ShaderSource({
|
defines : ['VECTOR_TILE'],
|
sources : [ShadowVolumeFS]
|
});
|
|
primitive._spStencil = ShaderProgram.fromCache({
|
context : context,
|
vertexShaderSource : vs,
|
fragmentShaderSource : fs,
|
attributeLocations : attributeLocations
|
});
|
|
fsSource = ShaderSource.replaceMain(fsSource, 'czm_non_pick_main');
|
fsSource =
|
fsSource + '\n' +
|
'void main() \n' +
|
'{ \n' +
|
' czm_non_pick_main(); \n' +
|
' gl_FragColor = ' + pickId + '; \n' +
|
'} \n';
|
|
var pickVS = new ShaderSource({
|
sources : [vsSource]
|
});
|
var pickFS = new ShaderSource({
|
defines : ['VECTOR_TILE'],
|
sources : [fsSource]
|
});
|
primitive._spPick = ShaderProgram.fromCache({
|
context : context,
|
vertexShaderSource : pickVS,
|
fragmentShaderSource : pickFS,
|
attributeLocations : attributeLocations
|
});
|
}
|
|
function getStencilPreloadRenderState(mask3DTiles) {
|
var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS;
|
return {
|
colorMask : {
|
red : false,
|
green : false,
|
blue : false,
|
alpha : false
|
},
|
stencilTest : {
|
enabled : true,
|
frontFunction : stencilFunction,
|
frontOperation : {
|
fail : StencilOperation.KEEP,
|
zFail : StencilOperation.DECREMENT_WRAP,
|
zPass : StencilOperation.DECREMENT_WRAP
|
},
|
backFunction : stencilFunction,
|
backOperation : {
|
fail : StencilOperation.KEEP,
|
zFail : StencilOperation.INCREMENT_WRAP,
|
zPass : StencilOperation.INCREMENT_WRAP
|
},
|
reference : StencilConstants.CESIUM_3D_TILE_MASK,
|
mask : StencilConstants.CESIUM_3D_TILE_MASK
|
},
|
stencilMask : StencilConstants.CLASSIFICATION_MASK,
|
depthTest : {
|
enabled : false
|
},
|
depthMask : false
|
};
|
}
|
|
function getStencilDepthRenderState(mask3DTiles) {
|
var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS;
|
return {
|
colorMask : {
|
red : false,
|
green : false,
|
blue : false,
|
alpha : false
|
},
|
stencilTest : {
|
enabled : true,
|
frontFunction : stencilFunction,
|
frontOperation : {
|
fail : StencilOperation.KEEP,
|
zFail : StencilOperation.KEEP,
|
zPass : StencilOperation.INCREMENT_WRAP
|
},
|
backFunction : stencilFunction,
|
backOperation : {
|
fail : StencilOperation.KEEP,
|
zFail : StencilOperation.KEEP,
|
zPass : StencilOperation.DECREMENT_WRAP
|
},
|
reference : StencilConstants.CESIUM_3D_TILE_MASK,
|
mask : StencilConstants.CESIUM_3D_TILE_MASK
|
},
|
stencilMask : StencilConstants.CLASSIFICATION_MASK,
|
depthTest : {
|
enabled : true,
|
func : DepthFunction.LESS_OR_EQUAL
|
},
|
depthMask : false
|
};
|
}
|
|
var colorRenderState = {
|
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 : 0,
|
mask : StencilConstants.CLASSIFICATION_MASK
|
},
|
stencilMask : StencilConstants.CLASSIFICATION_MASK,
|
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 : 0,
|
mask : StencilConstants.CLASSIFICATION_MASK
|
},
|
stencilMask : StencilConstants.CLASSIFICATION_MASK,
|
depthTest : {
|
enabled : false
|
},
|
depthMask : false
|
};
|
|
function createRenderStates(primitive) {
|
if (defined(primitive._rsStencilPreloadPass)) {
|
return;
|
}
|
|
primitive._rsStencilPreloadPass = RenderState.fromCache(getStencilPreloadRenderState(false));
|
primitive._rsStencilPreloadPass3DTiles = RenderState.fromCache(getStencilPreloadRenderState(true));
|
primitive._rsStencilDepthPass = RenderState.fromCache(getStencilDepthRenderState(false));
|
primitive._rsStencilDepthPass3DTiles = RenderState.fromCache(getStencilDepthRenderState(true));
|
primitive._rsColorPass = RenderState.fromCache(colorRenderState);
|
primitive._rsPickPass = RenderState.fromCache(pickRenderState);
|
}
|
|
var modifiedModelViewScratch = new Matrix4();
|
var rtcScratch = new Cartesian3();
|
|
function createUniformMap(primitive, context) {
|
if (defined(primitive._uniformMap)) {
|
return;
|
}
|
|
var uniformMap = {
|
u_modifiedModelViewProjection : function() {
|
var viewMatrix = context.uniformState.view;
|
var projectionMatrix = context.uniformState.projection;
|
Matrix4.clone(viewMatrix, modifiedModelViewScratch);
|
Matrix4.multiplyByPoint(modifiedModelViewScratch, primitive._center, rtcScratch);
|
Matrix4.setTranslation(modifiedModelViewScratch, rtcScratch, modifiedModelViewScratch);
|
Matrix4.multiply(projectionMatrix, modifiedModelViewScratch, modifiedModelViewScratch);
|
return modifiedModelViewScratch;
|
},
|
u_highlightColor : function() {
|
return primitive._highlightColor;
|
}
|
};
|
|
primitive._uniformMap = primitive._batchTable.getUniformMapCallback()(uniformMap);
|
}
|
|
function copyIndicesCPU(indices, newIndices, currentOffset, offsets, counts, batchIds, batchIdLookUp) {
|
var sizeInBytes = indices.constructor.BYTES_PER_ELEMENT;
|
|
var batchedIdsLength = batchIds.length;
|
for (var j = 0; j < batchedIdsLength; ++j) {
|
var batchedId = batchIds[j];
|
var index = batchIdLookUp[batchedId];
|
var offset = offsets[index];
|
var count = counts[index];
|
|
var subarray = new indices.constructor(indices.buffer, sizeInBytes * offset, count);
|
newIndices.set(subarray, currentOffset);
|
|
offsets[index] = currentOffset;
|
currentOffset += count;
|
}
|
|
return currentOffset;
|
}
|
|
function rebatchCPU(primitive, batchedIndices) {
|
var indices = primitive._indices;
|
var indexOffsets = primitive._indexOffsets;
|
var indexCounts = primitive._indexCounts;
|
var batchIdLookUp = primitive._batchIdLookUp;
|
|
var newIndices = new indices.constructor(indices.length);
|
|
var current = batchedIndices.pop();
|
var newBatchedIndices = [current];
|
|
var currentOffset = copyIndicesCPU(indices, newIndices, 0, indexOffsets, indexCounts, current.batchIds, batchIdLookUp);
|
|
current.offset = 0;
|
current.count = currentOffset;
|
|
while (batchedIndices.length > 0) {
|
var next = batchedIndices.pop();
|
if (Color.equals(next.color, current.color)) {
|
currentOffset = copyIndicesCPU(indices, newIndices, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp);
|
current.batchIds = current.batchIds.concat(next.batchIds);
|
current.count = currentOffset - current.offset;
|
} else {
|
var offset = currentOffset;
|
currentOffset = copyIndicesCPU(indices, newIndices, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp);
|
|
next.offset = offset;
|
next.count = currentOffset - offset;
|
newBatchedIndices.push(next);
|
current = next;
|
}
|
}
|
|
primitive._va.indexBuffer.copyFromArrayView(newIndices);
|
|
primitive._indices = newIndices;
|
primitive._batchedIndices = newBatchedIndices;
|
}
|
|
function copyIndicesGPU(readBuffer, writeBuffer, currentOffset, offsets, counts, batchIds, batchIdLookUp) {
|
var sizeInBytes = readBuffer.bytesPerIndex;
|
|
var batchedIdsLength = batchIds.length;
|
for (var j = 0; j < batchedIdsLength; ++j) {
|
var batchedId = batchIds[j];
|
var index = batchIdLookUp[batchedId];
|
var offset = offsets[index];
|
var count = counts[index];
|
|
writeBuffer.copyFromBuffer(readBuffer, offset * sizeInBytes, currentOffset * sizeInBytes, count * sizeInBytes);
|
|
offsets[index] = currentOffset;
|
currentOffset += count;
|
}
|
|
return currentOffset;
|
}
|
|
function rebatchGPU(primitive, batchedIndices) {
|
var indexOffsets = primitive._indexOffsets;
|
var indexCounts = primitive._indexCounts;
|
var batchIdLookUp = primitive._batchIdLookUp;
|
|
var current = batchedIndices.pop();
|
var newBatchedIndices = [current];
|
|
var readBuffer = primitive._va.indexBuffer;
|
var writeBuffer = primitive._vaSwap.indexBuffer;
|
|
var currentOffset = copyIndicesGPU(readBuffer, writeBuffer, 0, indexOffsets, indexCounts, current.batchIds, batchIdLookUp);
|
|
current.offset = 0;
|
current.count = currentOffset;
|
|
while (batchedIndices.length > 0) {
|
var next = batchedIndices.pop();
|
if (Color.equals(next.color, current.color)) {
|
currentOffset = copyIndicesGPU(readBuffer, writeBuffer, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp);
|
current.batchIds = current.batchIds.concat(next.batchIds);
|
current.count = currentOffset - current.offset;
|
} else {
|
var offset = currentOffset;
|
currentOffset = copyIndicesGPU(readBuffer, writeBuffer, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp);
|
next.offset = offset;
|
next.count = currentOffset - offset;
|
newBatchedIndices.push(next);
|
current = next;
|
}
|
}
|
|
var temp = primitive._va;
|
primitive._va = primitive._vaSwap;
|
primitive._vaSwap = temp;
|
|
primitive._batchedIndices = newBatchedIndices;
|
}
|
|
function compareColors(a, b) {
|
return b.color.toRgba() - a.color.toRgba();
|
}
|
|
// PERFORMANCE_IDEA: For WebGL 2, we can use copyBufferSubData for buffer-to-buffer copies.
|
// PERFORMANCE_IDEA: Not supported, but we could use glMultiDrawElements here.
|
function rebatchCommands(primitive, context) {
|
if (!primitive._batchDirty) {
|
return false;
|
}
|
|
var batchedIndices = primitive._batchedIndices;
|
var length = batchedIndices.length;
|
|
var needToRebatch = false;
|
var colorCounts = {};
|
|
for (var i = 0; i < length; ++i) {
|
var color = batchedIndices[i].color;
|
var rgba = color.toRgba();
|
if (defined(colorCounts[rgba])) {
|
needToRebatch = true;
|
break;
|
} else {
|
colorCounts[rgba] = true;
|
}
|
}
|
|
if (!needToRebatch) {
|
primitive._batchDirty = false;
|
return false;
|
}
|
|
if (needToRebatch && !primitive.forceRebatch && primitive._framesSinceLastRebatch < 120) {
|
++primitive._framesSinceLastRebatch;
|
return;
|
}
|
|
batchedIndices.sort(compareColors);
|
|
if (context.webgl2) {
|
rebatchGPU(primitive, batchedIndices);
|
} else {
|
rebatchCPU(primitive, batchedIndices);
|
}
|
|
primitive._framesSinceLastRebatch = 0;
|
primitive._batchDirty = false;
|
primitive._pickCommandsDirty = true;
|
primitive._wireframeDirty = true;
|
return true;
|
}
|
|
function createColorCommands(primitive, context) {
|
var needsRebatch = rebatchCommands(primitive, context);
|
|
var commands = primitive._commands;
|
var batchedIndices = primitive._batchedIndices;
|
var length = batchedIndices.length;
|
var commandsLength = length * 3;
|
|
if (defined(commands) &&
|
!needsRebatch &&
|
commands.length === commandsLength) {
|
return;
|
}
|
|
commands.length = commandsLength;
|
|
var vertexArray = primitive._va;
|
var sp = primitive._sp;
|
var modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY);
|
var uniformMap = primitive._uniformMap;
|
var bv = primitive._boundingVolume;
|
|
for (var j = 0; j < length; ++j) {
|
var offset = batchedIndices[j].offset;
|
var count = batchedIndices[j].count;
|
|
var stencilPreloadCommand = commands[j * 3];
|
if (!defined(stencilPreloadCommand)) {
|
stencilPreloadCommand = commands[j * 3] = new DrawCommand({
|
owner : primitive
|
});
|
}
|
|
stencilPreloadCommand.vertexArray = vertexArray;
|
stencilPreloadCommand.modelMatrix = modelMatrix;
|
stencilPreloadCommand.offset = offset;
|
stencilPreloadCommand.count = count;
|
stencilPreloadCommand.renderState = primitive._rsStencilPreloadPass;
|
stencilPreloadCommand.shaderProgram = sp;
|
stencilPreloadCommand.uniformMap = uniformMap;
|
stencilPreloadCommand.boundingVolume = bv;
|
stencilPreloadCommand.cull = false;
|
stencilPreloadCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var stencilPreloadDerivedCommand = DrawCommand.shallowClone(stencilPreloadCommand, stencilPreloadCommand.derivedCommands.tileset);
|
stencilPreloadDerivedCommand.renderState = primitive._rsStencilPreloadPass3DTiles;
|
stencilPreloadDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
stencilPreloadCommand.derivedCommands.tileset = stencilPreloadDerivedCommand;
|
|
var stencilDepthCommand = commands[j * 3 + 1];
|
if (!defined(stencilDepthCommand)) {
|
stencilDepthCommand = commands[j * 3 + 1] = new DrawCommand({
|
owner : primitive
|
});
|
}
|
|
stencilDepthCommand.vertexArray = vertexArray;
|
stencilDepthCommand.modelMatrix = modelMatrix;
|
stencilDepthCommand.offset = offset;
|
stencilDepthCommand.count = count;
|
stencilDepthCommand.renderState = primitive._rsStencilDepthPass;
|
stencilDepthCommand.shaderProgram = sp;
|
stencilDepthCommand.uniformMap = uniformMap;
|
stencilDepthCommand.boundingVolume = bv;
|
stencilDepthCommand.cull = false;
|
stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var stencilDepthDerivedCommand = DrawCommand.shallowClone(stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset);
|
stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles;
|
stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand;
|
|
var colorCommand = commands[j * 3 + 2];
|
if (!defined(colorCommand)) {
|
colorCommand = commands[j * 3 + 2] = new DrawCommand({
|
owner : primitive
|
});
|
}
|
|
colorCommand.vertexArray = vertexArray;
|
colorCommand.modelMatrix = modelMatrix;
|
colorCommand.offset = offset;
|
colorCommand.count = count;
|
colorCommand.renderState = primitive._rsColorPass;
|
colorCommand.shaderProgram = sp;
|
colorCommand.uniformMap = uniformMap;
|
colorCommand.boundingVolume = bv;
|
colorCommand.cull = false;
|
colorCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var colorDerivedCommand = DrawCommand.shallowClone(colorCommand, colorCommand.derivedCommands.tileset);
|
colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
colorCommand.derivedCommands.tileset = colorDerivedCommand;
|
}
|
|
primitive._commandsDirty = true;
|
}
|
|
function createColorCommandsIgnoreShow(primitive, frameState) {
|
if (primitive.classificationType === ClassificationType.TERRAIN ||
|
!frameState.invertClassification ||
|
(defined(primitive._commandsIgnoreShow) && !primitive._commandsDirty)) {
|
return;
|
}
|
|
var commands = primitive._commands;
|
var commandsIgnoreShow = primitive._commandsIgnoreShow;
|
var spStencil = primitive._spStencil;
|
|
var commandsLength = commands.length;
|
var length = commandsIgnoreShow.length = commandsLength / 3 * 2;
|
|
var commandIndex = 0;
|
for (var j = 0; j < length; j += 2) {
|
var commandIgnoreShow = commandsIgnoreShow[j] = DrawCommand.shallowClone(commands[commandIndex], commandsIgnoreShow[j]);
|
commandIgnoreShow.shaderProgram = spStencil;
|
commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
|
|
commandIgnoreShow = commandsIgnoreShow[j + 1] = DrawCommand.shallowClone(commands[commandIndex + 1], commandsIgnoreShow[j + 1]);
|
commandIgnoreShow.shaderProgram = spStencil;
|
commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
|
|
commandIndex += 3;
|
}
|
|
primitive._commandsDirty = false;
|
}
|
|
function createPickCommands(primitive) {
|
if (!primitive._pickCommandsDirty) {
|
return;
|
}
|
|
var length = primitive._indexOffsets.length;
|
var pickCommands = primitive._pickCommands;
|
pickCommands.length = length * 3;
|
|
var vertexArray = primitive._va;
|
var spStencil = primitive._spStencil;
|
var spPick = primitive._spPick;
|
var modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY);
|
var uniformMap = primitive._uniformMap;
|
|
for (var j = 0; j < length; ++j) {
|
var offset = primitive._indexOffsets[j];
|
var count = primitive._indexCounts[j];
|
var bv = defined(primitive._boundingVolumes) ? primitive._boundingVolumes[j] : primitive.boundingVolume;
|
|
var stencilPreloadCommand = pickCommands[j * 3];
|
if (!defined(stencilPreloadCommand)) {
|
stencilPreloadCommand = pickCommands[j * 3] = new DrawCommand({
|
owner : primitive,
|
pickOnly : true
|
});
|
}
|
|
stencilPreloadCommand.vertexArray = vertexArray;
|
stencilPreloadCommand.modelMatrix = modelMatrix;
|
stencilPreloadCommand.offset = offset;
|
stencilPreloadCommand.count = count;
|
stencilPreloadCommand.renderState = primitive._rsStencilPreloadPass;
|
stencilPreloadCommand.shaderProgram = spStencil;
|
stencilPreloadCommand.uniformMap = uniformMap;
|
stencilPreloadCommand.boundingVolume = bv;
|
stencilPreloadCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var stencilPreloadDerivedCommand = DrawCommand.shallowClone(stencilPreloadCommand, stencilPreloadCommand.derivedCommands.tileset);
|
stencilPreloadDerivedCommand.renderState = primitive._rsStencilPreloadPass3DTiles;
|
stencilPreloadDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
stencilPreloadCommand.derivedCommands.tileset = stencilPreloadDerivedCommand;
|
|
var stencilDepthCommand = pickCommands[j * 3 + 1];
|
if (!defined(stencilDepthCommand)) {
|
stencilDepthCommand = pickCommands[j * 3 + 1] = new DrawCommand({
|
owner : primitive,
|
pickOnly : true
|
});
|
}
|
|
stencilDepthCommand.vertexArray = vertexArray;
|
stencilDepthCommand.modelMatrix = modelMatrix;
|
stencilDepthCommand.offset = offset;
|
stencilDepthCommand.count = count;
|
stencilDepthCommand.renderState = primitive._rsStencilDepthPass;
|
stencilDepthCommand.shaderProgram = spStencil;
|
stencilDepthCommand.uniformMap = uniformMap;
|
stencilDepthCommand.boundingVolume = bv;
|
stencilDepthCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var stencilDepthDerivedCommand = DrawCommand.shallowClone(stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset);
|
stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles;
|
stencilDepthDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand;
|
|
var colorCommand = pickCommands[j * 3 + 2];
|
if (!defined(colorCommand)) {
|
colorCommand = pickCommands[j * 3 + 2] = new DrawCommand({
|
owner : primitive,
|
pickOnly : true
|
});
|
}
|
|
colorCommand.vertexArray = vertexArray;
|
colorCommand.modelMatrix = modelMatrix;
|
colorCommand.offset = offset;
|
colorCommand.count = count;
|
colorCommand.renderState = primitive._rsPickPass;
|
colorCommand.shaderProgram = spPick;
|
colorCommand.uniformMap = uniformMap;
|
colorCommand.boundingVolume = bv;
|
colorCommand.pass = Pass.TERRAIN_CLASSIFICATION;
|
|
var colorDerivedCommand = DrawCommand.shallowClone(colorCommand, colorCommand.derivedCommands.tileset);
|
colorDerivedCommand.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
colorCommand.derivedCommands.tileset = colorDerivedCommand;
|
}
|
|
primitive._pickCommandsDirty = false;
|
}
|
|
/**
|
* Creates features for each mesh 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.
|
*/
|
Vector3DTilePrimitive.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 (mesh batch table color * color).
|
*
|
* @param {Boolean} enabled Whether to enable debug coloring.
|
* @param {Color} color The debug color.
|
*/
|
Vector3DTilePrimitive.prototype.applyDebugSettings = function(enabled, color) {
|
this._highlightColor = enabled ? color : this._constantColor;
|
};
|
|
function clearStyle(polygons, features) {
|
polygons._updatingAllCommands = true;
|
|
var batchIds = polygons._batchIds;
|
var length = batchIds.length;
|
var i;
|
|
for (i = 0; i < length; ++i) {
|
var batchId = batchIds[i];
|
var feature = features[batchId];
|
|
feature.show = true;
|
feature.color = Color.WHITE;
|
}
|
|
var batchedIndices = polygons._batchedIndices;
|
length = batchedIndices.length;
|
|
for (i = 0; i < length; ++i) {
|
batchedIndices[i].color = Color.clone(Color.WHITE);
|
}
|
|
polygons._updatingAllCommands = false;
|
polygons._batchDirty = true;
|
}
|
|
var scratchColor = new Color();
|
|
var DEFAULT_COLOR_VALUE = Color.WHITE;
|
var DEFAULT_SHOW_VALUE = true;
|
|
var complexExpressionReg = /\$/;
|
|
/**
|
* Apply a style to the content.
|
*
|
* @param {Cesium3DTileStyle} style The style.
|
* @param {Cesium3DTileFeature[]} features The array of features.
|
*/
|
Vector3DTilePrimitive.prototype.applyStyle = function(style, features) {
|
if (!defined(style)) {
|
clearStyle(this, features);
|
return;
|
}
|
|
var colorExpression = style.color;
|
var isSimpleStyle = colorExpression instanceof Expression && !complexExpressionReg.test(colorExpression.expression);
|
this._updatingAllCommands = isSimpleStyle;
|
|
var batchIds = this._batchIds;
|
var length = batchIds.length;
|
var i;
|
|
for (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;
|
}
|
|
if (isSimpleStyle) {
|
var batchedIndices = this._batchedIndices;
|
length = batchedIndices.length;
|
|
for (i = 0; i < length; ++i) {
|
batchedIndices[i].color = Color.clone(Color.WHITE);
|
}
|
|
this._updatingAllCommands = false;
|
this._batchDirty = true;
|
}
|
};
|
|
/**
|
* Call when updating the color of a mesh with batchId changes color. The meshes will need to be re-batched
|
* on the next update.
|
*
|
* @param {Number} batchId The batch id of the meshes whose color has changed.
|
* @param {Color} color The new polygon color.
|
*/
|
Vector3DTilePrimitive.prototype.updateCommands = function(batchId, color) {
|
if (this._updatingAllCommands) {
|
return;
|
}
|
|
var batchIdLookUp = this._batchIdLookUp;
|
var index = batchIdLookUp[batchId];
|
if (!defined(index)) {
|
return;
|
}
|
|
var indexOffsets = this._indexOffsets;
|
var indexCounts = this._indexCounts;
|
|
var offset = indexOffsets[index];
|
var count = indexCounts[index];
|
|
var batchedIndices = this._batchedIndices;
|
var length = batchedIndices.length;
|
|
var i;
|
for (i = 0; i < length; ++i) {
|
var batchedOffset = batchedIndices[i].offset;
|
var batchedCount = batchedIndices[i].count;
|
|
if (offset >= batchedOffset && offset < batchedOffset + batchedCount) {
|
break;
|
}
|
}
|
|
batchedIndices.push(new Vector3DTileBatch({
|
color : Color.clone(color),
|
offset : offset,
|
count : count,
|
batchIds : [batchId]
|
}));
|
|
var startIds = [];
|
var endIds = [];
|
|
var batchIds = batchedIndices[i].batchIds;
|
var batchIdsLength = batchIds.length;
|
|
for (var j = 0; j < batchIdsLength; ++j) {
|
var id = batchIds[j];
|
if (id === batchId) {
|
continue;
|
}
|
|
var offsetIndex = batchIdLookUp[id];
|
if (indexOffsets[offsetIndex] < offset) {
|
startIds.push(id);
|
} else {
|
endIds.push(id);
|
}
|
}
|
|
if (endIds.length !== 0) {
|
batchedIndices.push(new Vector3DTileBatch({
|
color : Color.clone(batchedIndices[i].color),
|
offset : offset + count,
|
count : batchedIndices[i].offset + batchedIndices[i].count - (offset + count),
|
batchIds : endIds
|
}));
|
}
|
|
if (startIds.length !== 0) {
|
batchedIndices[i].count = offset - batchedIndices[i].offset;
|
batchedIndices[i].batchIds = startIds;
|
} else {
|
batchedIndices.splice(i, 1);
|
}
|
|
this._batchDirty = true;
|
};
|
|
function queueCommands(primitive, frameState, commands, commandsIgnoreShow) {
|
var classificationType = primitive.classificationType;
|
var queueTerrainCommands = (classificationType !== ClassificationType.CESIUM_3D_TILE);
|
var queue3DTilesCommands = (classificationType !== ClassificationType.TERRAIN);
|
|
var commandList = frameState.commandList;
|
var commandLength = commands.length;
|
var command;
|
var i;
|
for (i = 0; i < commandLength; ++i) {
|
if (queueTerrainCommands) {
|
command = commands[i];
|
command.pass = Pass.TERRAIN_CLASSIFICATION;
|
commandList.push(command);
|
}
|
if (queue3DTilesCommands) {
|
command = commands[i].derivedCommands.tileset;
|
command.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION;
|
commandList.push(command);
|
}
|
}
|
|
if (!frameState.invertClassification || !defined(commandsIgnoreShow)) {
|
return;
|
}
|
|
commandLength = commandsIgnoreShow.length;
|
for (i = 0; i < commandLength; ++i) {
|
commandList.push(commandsIgnoreShow[i]);
|
}
|
}
|
|
function queueWireframeCommands(frameState, commands) {
|
var commandList = frameState.commandList;
|
var commandLength = commands.length;
|
for (var i = 0; i < commandLength; i += 3) {
|
var command = commands[i + 2];
|
command.pass = Pass.OPAQUE;
|
commandList.push(command);
|
}
|
}
|
|
function updateWireframe(primitive) {
|
var earlyExit = primitive.debugWireframe === primitive._debugWireframe;
|
earlyExit = earlyExit && !(primitive.debugWireframe && primitive._wireframeDirty);
|
if (earlyExit) {
|
return;
|
}
|
|
if (!defined(primitive._rsWireframe)) {
|
primitive._rsWireframe = RenderState.fromCache({});
|
}
|
|
var rs;
|
var type;
|
|
if (primitive.debugWireframe) {
|
rs = primitive._rsWireframe;
|
type = PrimitiveType.LINES;
|
} else {
|
rs = primitive._rsColorPass;
|
type = PrimitiveType.TRIANGLES;
|
}
|
|
var commands = primitive._commands;
|
var commandLength = commands.length;
|
for (var i = 0; i < commandLength; i += 3) {
|
var command = commands[i + 2];
|
command.renderState = rs;
|
command.primitiveType = type;
|
}
|
|
primitive._debugWireframe = primitive.debugWireframe;
|
primitive._wireframeDirty = false;
|
}
|
|
/**
|
* Updates the batches and queues the commands for rendering.
|
*
|
* @param {FrameState} frameState The current frame state.
|
*/
|
Vector3DTilePrimitive.prototype.update = function(frameState) {
|
var context = frameState.context;
|
|
createVertexArray(this, context);
|
createShaders(this, context);
|
createRenderStates(this);
|
createUniformMap(this, context);
|
|
var passes = frameState.passes;
|
if (passes.render) {
|
createColorCommands(this, context);
|
createColorCommandsIgnoreShow(this, frameState);
|
updateWireframe(this);
|
|
if (this._debugWireframe) {
|
queueWireframeCommands(frameState, this._commands);
|
} else {
|
queueCommands(this, frameState, this._commands, this._commandsIgnoreShow);
|
}
|
}
|
|
if (passes.pick) {
|
createPickCommands(this);
|
queueCommands(this, frameState, this._pickCommands);
|
}
|
};
|
|
/**
|
* 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>.
|
*/
|
Vector3DTilePrimitive.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.
|
*/
|
Vector3DTilePrimitive.prototype.destroy = function() {
|
this._va = this._va && this._va.destroy();
|
this._sp = this._sp && this._sp.destroy();
|
this._spPick = this._spPick && this._spPick.destroy();
|
this._vaSwap = this._vaSwap && this._vaSwap.destroy();
|
return destroyObject(this);
|
};
|
export default Vector3DTilePrimitive;
|