@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
133 lines (111 loc) • 3.97 kB
JavaScript
import defined from "../../Core/defined.js";
import RuntimeError from "../../Core/RuntimeError.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
import ModelUtility from "./ModelUtility.js";
/**
* The classification pipeline stage is responsible for batching features
* together to be rendered by the {@link ClassificationModelDrawCommand}.
*
* @namespace ClassificationPipelineStage
*
* @private
*/
const ClassificationPipelineStage = {
name: "ClassificationPipelineStage", // Helps with debugging
};
/**
* Process a primitive. This modifies the following parts of the render resources:
*
* <ul>
* <li>adds a define to the shader to indicate that the primitive classifies other assets</li>
* <li>adds arrays containing batch lengths and offsets to the primitive's resources
* </ul>
*
* <p>
* See {@link ClassificationModelDrawCommand} for the use of the batch offsets and lengths.
* </p>
*
* @param {PrimitiveRenderResources} renderResources The render resources for this primitive.
* @param {ModelComponents.Primitive} primitive The primitive.
* @param {FrameState} frameState The frame state.
*
* @private
*/
ClassificationPipelineStage.process = function (
renderResources,
primitive,
frameState,
) {
const shaderBuilder = renderResources.shaderBuilder;
shaderBuilder.addDefine(
"HAS_CLASSIFICATION",
undefined,
ShaderDestination.BOTH,
);
const runtimePrimitive = renderResources.runtimePrimitive;
if (!defined(runtimePrimitive.batchLengths)) {
createClassificationBatches(primitive, runtimePrimitive);
}
};
function createClassificationBatches(primitive, runtimePrimitive) {
const positionAttribute = ModelUtility.getAttributeBySemantic(
primitive,
VertexAttributeSemantic.POSITION,
);
if (!defined(positionAttribute)) {
throw new RuntimeError(
"Primitives must have a position attribute to be used for classification.",
);
}
let indicesArray;
const indices = primitive.indices;
const hasIndices = defined(indices);
if (hasIndices) {
indicesArray = indices.typedArray;
// Unload the typed array. This is just a pointer to the array in
// the index buffer loader.
indices.typedArray = undefined;
}
const count = hasIndices ? indices.count : positionAttribute.count;
const featureIdAttribute = ModelUtility.getAttributeBySemantic(
primitive,
VertexAttributeSemantic.FEATURE_ID,
0,
);
// If there are no feature IDs, render the primitive in a single batch.
if (!defined(featureIdAttribute)) {
runtimePrimitive.batchLengths = [count];
runtimePrimitive.batchOffsets = [0];
return;
}
const featureIds = featureIdAttribute.typedArray;
// Unload the typed array. This is just a pointer to the array in
// the vertex buffer loader, so if the typed array is shared by
// multiple primitives (i.e. multiple instances of the same mesh),
// this will not affect the other primitives.
featureIdAttribute.typedArray = undefined;
const batchLengths = [];
const batchOffsets = [0];
const firstIndex = hasIndices ? indicesArray[0] : 0;
let currentBatchId = featureIds[firstIndex];
let currentOffset = 0;
for (let i = 1; i < count; i++) {
const index = hasIndices ? indicesArray[i] : i;
const batchId = featureIds[index];
if (batchId !== currentBatchId) {
// Store the length of this batch and begin counting the next one.
const batchLength = i - currentOffset;
const newOffset = i;
batchLengths.push(batchLength);
batchOffsets.push(newOffset);
currentOffset = newOffset;
currentBatchId = batchId;
}
}
const finalBatchLength = count - currentOffset;
batchLengths.push(finalBatchLength);
runtimePrimitive.batchLengths = batchLengths;
runtimePrimitive.batchOffsets = batchOffsets;
}
export default ClassificationPipelineStage;