You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1288 lines
35 KiB
JavaScript

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._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(
false,
undefined,
true
)(ShadowVolumeFS);
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 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.DECREMENT_WRAP,
zPass: StencilOperation.KEEP,
},
backFunction: stencilFunction,
backOperation: {
fail: StencilOperation.KEEP,
zFail: StencilOperation.INCREMENT_WRAP,
zPass: StencilOperation.KEEP,
},
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.ZERO,
zFail: StencilOperation.ZERO,
zPass: StencilOperation.ZERO,
},
backFunction: StencilFunction.NOT_EQUAL,
backOperation: {
fail: StencilOperation.ZERO,
zFail: StencilOperation.ZERO,
zPass: StencilOperation.ZERO,
},
reference: 0,
mask: StencilConstants.CLASSIFICATION_MASK,
},
stencilMask: StencilConstants.CLASSIFICATION_MASK,
depthTest: {
enabled: false,
},
depthMask: false,
blending: BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
};
var pickRenderState = {
stencilTest: {
enabled: true,
frontFunction: StencilFunction.NOT_EQUAL,
frontOperation: {
fail: StencilOperation.ZERO,
zFail: StencilOperation.ZERO,
zPass: StencilOperation.ZERO,
},
backFunction: StencilFunction.NOT_EQUAL,
backOperation: {
fail: StencilOperation.ZERO,
zFail: StencilOperation.ZERO,
zPass: StencilOperation.ZERO,
},
reference: 0,
mask: StencilConstants.CLASSIFICATION_MASK,
},
stencilMask: StencilConstants.CLASSIFICATION_MASK,
depthTest: {
enabled: false,
},
depthMask: false,
};
function createRenderStates(primitive) {
if (defined(primitive._rsStencilDepthPass)) {
return;
}
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 * 2;
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 stencilDepthCommand = commands[j * 2];
if (!defined(stencilDepthCommand)) {
stencilDepthCommand = commands[j * 2] = 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 * 2 + 1];
if (!defined(colorCommand)) {
colorCommand = commands[j * 2 + 1] = 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 / 2);
var commandIndex = 0;
for (var j = 0; j < length; ++j) {
var commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone(
commands[commandIndex],
commandsIgnoreShow[j]
));
commandIgnoreShow.shaderProgram = spStencil;
commandIgnoreShow.pass = Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW;
commandIndex += 2;
}
primitive._commandsDirty = false;
}
function createPickCommands(primitive) {
if (!primitive._pickCommandsDirty) {
return;
}
var length = primitive._indexOffsets.length;
var pickCommands = primitive._pickCommands;
pickCommands.length = length * 2;
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 stencilDepthCommand = pickCommands[j * 2];
if (!defined(stencilDepthCommand)) {
stencilDepthCommand = pickCommands[j * 2] = 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 * 2 + 1];
if (!defined(colorCommand)) {
colorCommand = pickCommands[j * 2 + 1] = 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 += 2) {
var command = commands[i + 1];
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 += 2) {
var command = commands[i + 1];
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;