import Buffer from "../../Renderer/Buffer.js";
import BufferUsage from "../../Renderer/BufferUsage.js";
import Color from "../../Core/Color.js";
import combine from "../../Core/combine.js";
import ComponentDatatype from "../../Core/ComponentDatatype.js";
import defaultValue from "../../Core/defaultValue.js";
import defined from "../../Core/defined.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
/**
* The picking pipeline stage is responsible for handling picking of primitives.
*
* @namespace PickingPipelineStage
* @private
*/
var PickingPipelineStage = {};
PickingPipelineStage.name = "PickingPipelineStage"; // Helps with debugging
/**
* Process a primitive. This modifies the following parts of the render resources:
*
* - adds attribute and varying declaration for the pick color vertex attribute in the vertex shader for instanced meshes
* - adds declaration for the pick color uniform for non-instanced meshes
* - adds defines in the shader for when picking is enabled
* - creates the pick ID objects in the context
*
* @param {PrimitiveRenderResources} renderResources The render resources for this primitive.
* @param {ModelComponents.Primitive} primitive The primitive.
* @param {FrameState} frameState The frame state.
*/
PickingPipelineStage.process = function (
renderResources,
primitive,
frameState
) {
var context = frameState.context;
var runtimeNode = renderResources.runtimeNode;
var shaderBuilder = renderResources.shaderBuilder;
var model = renderResources.model;
var instances = runtimeNode.node.instances;
if (renderResources.hasFeatureIds) {
processPickTexture(renderResources, primitive, instances, context);
} else if (defined(instances)) {
// For instanced meshes, a pick color vertex attribute is used.
processInstancedPickIds(renderResources, instances, context);
} else {
// For non-instanced meshes, a pick color uniform is used.
var pickObject = buildPickObject(renderResources);
var pickId = context.createPickId(pickObject);
model._resources.push(pickId);
shaderBuilder.addUniform(
"vec4",
"czm_pickColor",
ShaderDestination.FRAGMENT
);
var uniformMap = renderResources.uniformMap;
uniformMap.czm_pickColor = function () {
return pickId.color;
};
renderResources.pickId = "czm_pickColor";
}
};
/**
* @private
*/
function buildPickObject(renderResources, instanceId) {
var model = renderResources.model;
var detailPickObject = {
model: model,
node: renderResources.runtimeNode,
primitive: renderResources.runtimePrimitive,
};
var content = model.content;
var pickObject;
if (defined(content)) {
// For 3D Tiles, the pick object's content and primitive are set to the Cesium3DTileContent that owns the model
// and the tileset it belongs to, respectively. The detail pick object is returned under the detail key.
pickObject = {
content: content,
primitive: content.tileset,
detail: detailPickObject,
};
} else {
// For models, the model itself is returned as the primitive, with the detail pick object under the detail key.
pickObject = {
primitive: model,
detail: detailPickObject,
};
}
if (defined(instanceId)) {
// For instanced models, an instanceId property is added to the pick object.
pickObject.instanceId = instanceId;
}
return pickObject;
}
function processPickTexture(renderResources, primitive, instances) {
var model = renderResources.model;
var featureTableId;
var featureIdAttribute;
var featureIdAttributeIndex = model.featureIdAttributeIndex;
if (defined(model.featureTableId)) {
// Extract the Feature Table ID from the Cesium3DTileContent.
featureTableId = model.featureTableId;
} else if (defined(instances)) {
// Extract the Feature Table ID from the instanced Feature ID attributes.
featureIdAttribute = instances.featureIdAttributes[featureIdAttributeIndex];
featureTableId = featureIdAttribute.propertyTableId;
} else if (primitive.featureIdTextures.length > 0) {
// Extract the Feature Table ID from the instanced Feature ID textures.
var featureIdTextureIndex = model.featureIdTextureIndex;
var featureIdTexture = primitive.featureIdTextures[featureIdTextureIndex];
featureTableId = featureIdTexture.propertyTableId;
} else {
// Extract the Feature Table ID from the primitive Feature ID attributes.
featureIdAttribute = primitive.featureIdAttributes[featureIdAttributeIndex];
featureTableId = featureIdAttribute.propertyTableId;
}
var featureTable = model.featureTables[featureTableId];
var shaderBuilder = renderResources.shaderBuilder;
shaderBuilder.addUniform(
"sampler2D",
"model_pickTexture",
ShaderDestination.FRAGMENT
);
var batchTexture = featureTable.batchTexture;
var pickingUniforms = {
model_pickTexture: function () {
return defaultValue(
batchTexture.pickTexture,
batchTexture.defaultTexture
);
},
};
renderResources.uniformMap = combine(
pickingUniforms,
renderResources.uniformMap
);
// The feature ID is ignored if it is greater than the number of features.
renderResources.pickId =
"((feature.id < int(model_featuresLength)) ? texture2D(model_pickTexture, feature.st) : vec4(0.0))";
}
function processInstancedPickIds(renderResources, instances, context) {
var instanceCount = renderResources.instanceCount;
var pickIds = new Array(instanceCount);
var pickIdsTypedArray = new Uint8Array(instanceCount * 4);
var model = renderResources.model;
var modelResources = model._resources;
for (var i = 0; i < instanceCount; i++) {
var pickObject = buildPickObject(renderResources, i);
var pickId = context.createPickId(pickObject);
modelResources.push(pickId);
pickIds[i] = pickId;
var pickColor = pickId.color;
pickIdsTypedArray[i * 4 + 0] = Color.floatToByte(pickColor.red);
pickIdsTypedArray[i * 4 + 1] = Color.floatToByte(pickColor.green);
pickIdsTypedArray[i * 4 + 2] = Color.floatToByte(pickColor.blue);
pickIdsTypedArray[i * 4 + 3] = Color.floatToByte(pickColor.alpha);
}
var pickIdsBuffer = Buffer.createVertexBuffer({
context: context,
typedArray: pickIdsTypedArray,
usage: BufferUsage.STATIC_DRAW,
});
// Destruction of resources allocated by the ModelExperimental is handled by ModelExperimental.destroy().
pickIdsBuffer.vertexArrayDestroyable = false;
modelResources.push(pickIdsBuffer);
var pickIdsVertexAttribute = {
index: renderResources.attributeIndex++,
vertexBuffer: pickIdsBuffer,
componentsPerAttribute: 4,
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
normalize: true,
offsetInBytes: 0,
strideInBytes: 0,
instanceDivisor: 1,
};
renderResources.attributes.push(pickIdsVertexAttribute);
var shaderBuilder = renderResources.shaderBuilder;
shaderBuilder.addDefine("USE_PICKING", undefined, ShaderDestination.BOTH);
shaderBuilder.addAttribute("vec4", "a_pickColor");
shaderBuilder.addVarying("vec4", "v_pickColor");
renderResources.pickId = "v_pickColor";
}
export default PickingPipelineStage;