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: * * @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;