@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
381 lines (328 loc) • 11.5 kB
JavaScript
import Check from "../../Core/Check.js";
import Frozen from "../../Core/Frozen.js";
import defined from "../../Core/defined.js";
import PrimitiveType from "../../Core/PrimitiveType.js";
import SceneMode from "../SceneMode.js";
import AlphaPipelineStage from "./AlphaPipelineStage.js";
import BatchTexturePipelineStage from "./BatchTexturePipelineStage.js";
import ClassificationPipelineStage from "./ClassificationPipelineStage.js";
import CPUStylingPipelineStage from "./CPUStylingPipelineStage.js";
import CustomShaderMode from "./CustomShaderMode.js";
import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js";
import DequantizationPipelineStage from "./DequantizationPipelineStage.js";
import EdgeDetectionPipelineStage from "./EdgeDetectionPipelineStage.js";
import EdgeVisibilityPipelineStage from "./EdgeVisibilityPipelineStage.js";
import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
import GeometryPipelineStage from "./GeometryPipelineStage.js";
import ImageryPipelineStage from "./ImageryPipelineStage.js";
import LightingPipelineStage from "./LightingPipelineStage.js";
import MaterialPipelineStage from "./MaterialPipelineStage.js";
import MetadataPickingPipelineStage from "./MetadataPickingPipelineStage.js";
import MetadataPipelineStage from "./MetadataPipelineStage.js";
import ModelUtility from "./ModelUtility.js";
import MorphTargetsPipelineStage from "./MorphTargetsPipelineStage.js";
import PickingPipelineStage from "./PickingPipelineStage.js";
import PointCloudStylingPipelineStage from "./PointCloudStylingPipelineStage.js";
import PrimitiveOutlinePipelineStage from "./PrimitiveOutlinePipelineStage.js";
import PrimitiveStatisticsPipelineStage from "./PrimitiveStatisticsPipelineStage.js";
import SceneMode2DPipelineStage from "./SceneMode2DPipelineStage.js";
import SelectedFeatureIdPipelineStage from "./SelectedFeatureIdPipelineStage.js";
import SkinningPipelineStage from "./SkinningPipelineStage.js";
import VerticalExaggerationPipelineStage from "./VerticalExaggerationPipelineStage.js";
import WireframePipelineStage from "./WireframePipelineStage.js";
import oneTimeWarning from "../../Core/oneTimeWarning.js";
/**
* In memory representation of a single primitive, that is, a primitive
* and its corresponding mesh.
*
* @param {object} options An object containing the following options:
* @param {ModelComponents.Primitive} options.primitive The primitive component.
* @param {ModelComponents.Node} options.node The node that this primitive belongs to.
* @param {Model} options.model The {@link Model} this primitive belongs to.
*
* @alias ModelRuntimePrimitive
* @constructor
*
* @private
*/
function ModelRuntimePrimitive(options) {
options = options ?? Frozen.EMPTY_OBJECT;
const primitive = options.primitive;
const node = options.node;
const model = options.model;
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("options.primitive", primitive);
Check.typeOf.object("options.node", node);
Check.typeOf.object("options.model", model);
//>>includeEnd('debug');
/**
* The primitive component associated with this primitive.
*
* @type {ModelComponents.Primitive}
*
* @private
*/
this.primitive = primitive;
/**
* A reference to the node this primitive belongs to.
*
* @type {ModelComponents.Node}
*
* @private
*/
this.node = node;
/**
* A reference to the model
*
* @type {Model}
*
* @private
*/
this.model = model;
/**
* Pipeline stages to apply to this primitive. This
* is an array of classes, each with a static method called
* <code>process()</code>
*
* @type {object[]}
* @readonly
*
* @private
*/
this.pipelineStages = [];
/**
* The generated {@link ModelDrawCommand} or {@link ClassificationModelDrawCommand}
* associated with this primitive.
*
* @type {ModelDrawCommand|ClassificationModelDrawCommand}
*
* @private
*/
this.drawCommand = undefined;
/**
* The bounding sphere of this primitive in object-space.
*
* @type {BoundingSphere}
*
* @private
*/
this.boundingSphere = undefined;
/**
* The bounding sphere of this primitive in 2D world space.
*
* @type {BoundingSphere}
*
* @private
*/
this.boundingSphere2D = undefined;
/**
* A buffer containing the primitive's positions projected to 2D world
* coordinates. This is generated by SceneMode2DPipelineStage and used for
* rendering in 2D / CV mode. The memory is managed by Model; this is just
* a reference.
*
* @type {Buffer}
*
* @private
*/
this.positionBuffer2D = undefined;
/**
* An array containing the lengths of the vertex batches for classification.
* Vertices with the same feature ID are batched together, and each batch is
* drawn with a different draw command in order to properly classify other
* assets.
* <p>
* This is generated by ClassificationPipelineStage. The memory is managed by
* Model; this is just a reference.
* </p>
*
* @type {number[]}
*
* @private
*/
this.batchLengths = undefined;
/**
* An array containing the offsets of the vertex batches for classification.
* Vertices with the same feature ID are batched together, and each batch is
* drawn with a different draw command in order to properly classify other
* assets.
* <p>
* This is generated by ClassificationPipelineStage. The memory is managed by
* Model; this is just a reference.
* </p>
*
* @type {number[]}
*
* @private
*/
this.batchOffsets = undefined;
/**
* Update stages to apply to this primitive.
*
* @type {object[]}
* @readonly
*
* @private
*/
this.updateStages = [];
}
/**
* Configure the primitive pipeline stages. If the pipeline needs to be re-run,
* call this method again to ensure the correct sequence of pipeline stages are
* used.
*
* @param {FrameState} frameState The frame state.
*
* @private
*/
ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
const pipelineStages = this.pipelineStages;
pipelineStages.length = 0;
const primitive = this.primitive;
const node = this.node;
const model = this.model;
const customShader = model.customShader;
const style = model.style;
const useWebgl2 = frameState.context.webgl2;
const mode = frameState.mode;
const use2D =
mode !== SceneMode.SCENE3D && !frameState.scene3DOnly && model._projectTo2D;
const hasVerticalExaggeration =
frameState.verticalExaggeration !== 1.0 && model.hasVerticalExaggeration;
const hasMorphTargets =
defined(primitive.morphTargets) && primitive.morphTargets.length > 0;
const hasSkinning = defined(node.skin);
// Check whether the model is part of a `Model3DTileContent` that
// belongs to a tileset that has imagery layers. If this is the
// case, then the `ImageryPipelineStage` will be required.
const hasImageryLayers = defined(model.imageryLayers);
const hasCustomShader = defined(customShader);
const hasCustomFragmentShader =
hasCustomShader && defined(customShader.fragmentShaderText);
const materialsEnabled =
!hasCustomFragmentShader ||
customShader.mode !== CustomShaderMode.REPLACE_MATERIAL;
const hasQuantization = ModelUtility.hasQuantizedAttributes(
primitive.attributes,
);
const generateWireframeIndices =
model.debugWireframe &&
PrimitiveType.isTriangles(primitive.primitiveType) &&
// Generating index buffers for wireframes is always possible in WebGL2.
// However, this will only work in WebGL1 if the model was constructed with
// enableDebugWireframe set to true.
(model._enableDebugWireframe || useWebgl2);
const pointCloudShading = model.pointCloudShading;
const hasAttenuation =
defined(pointCloudShading) && pointCloudShading.attenuation;
const hasPointCloudBackFaceCulling =
defined(pointCloudShading) && pointCloudShading.backFaceCulling;
const hasPointCloudStyle =
primitive.primitiveType === PrimitiveType.POINTS &&
(defined(style) || hasAttenuation || hasPointCloudBackFaceCulling);
const hasOutlines =
model._enableShowOutline && defined(primitive.outlineCoordinates);
const hasEdgeVisibility = defined(primitive.edgeVisibility);
const featureIdFlags = inspectFeatureIds(model, node, primitive);
const hasClassification = defined(model.classificationType);
// Start of pipeline -----------------------------------------------------
if (use2D) {
pipelineStages.push(SceneMode2DPipelineStage);
}
pipelineStages.push(GeometryPipelineStage);
if (generateWireframeIndices) {
pipelineStages.push(WireframePipelineStage);
}
if (hasClassification) {
pipelineStages.push(ClassificationPipelineStage);
}
if (hasMorphTargets) {
pipelineStages.push(MorphTargetsPipelineStage);
}
if (hasSkinning) {
pipelineStages.push(SkinningPipelineStage);
}
if (hasPointCloudStyle) {
pipelineStages.push(PointCloudStylingPipelineStage);
}
if (hasQuantization) {
pipelineStages.push(DequantizationPipelineStage);
}
if (hasImageryLayers) {
if (hasOutlines) {
oneTimeWarning(
"outlines-and-draping",
"Primitive outlines disable imagery draping",
);
} else {
pipelineStages.push(ImageryPipelineStage);
}
}
if (materialsEnabled) {
pipelineStages.push(MaterialPipelineStage);
}
// These stages are always run to ensure structs
// are declared to avoid compilation errors.
pipelineStages.push(FeatureIdPipelineStage);
pipelineStages.push(MetadataPipelineStage);
pipelineStages.push(MetadataPickingPipelineStage);
if (featureIdFlags.hasPropertyTable) {
pipelineStages.push(SelectedFeatureIdPipelineStage);
pipelineStages.push(BatchTexturePipelineStage);
pipelineStages.push(CPUStylingPipelineStage);
}
if (hasVerticalExaggeration) {
pipelineStages.push(VerticalExaggerationPipelineStage);
}
if (hasCustomShader) {
pipelineStages.push(CustomShaderPipelineStage);
}
pipelineStages.push(LightingPipelineStage);
if (model.allowPicking) {
pipelineStages.push(PickingPipelineStage);
}
if (hasOutlines) {
pipelineStages.push(PrimitiveOutlinePipelineStage);
}
if (hasEdgeVisibility) {
// Indicate to Scene (after primitive updates) that the edge MRT should be enabled.
frameState.edgeVisibilityRequested = true;
pipelineStages.push(EdgeVisibilityPipelineStage);
pipelineStages.push(EdgeDetectionPipelineStage);
}
pipelineStages.push(AlphaPipelineStage);
pipelineStages.push(PrimitiveStatisticsPipelineStage);
return;
};
function inspectFeatureIds(model, node, primitive) {
let featureIds;
// Check instances first, as this is the most specific type of
// feature ID
if (defined(node.instances)) {
featureIds = ModelUtility.getFeatureIdsByLabel(
node.instances.featureIds,
model.instanceFeatureIdLabel,
);
if (defined(featureIds)) {
return {
hasFeatureIds: true,
hasPropertyTable: defined(featureIds.propertyTableId),
};
}
}
featureIds = ModelUtility.getFeatureIdsByLabel(
primitive.featureIds,
model.featureIdLabel,
);
if (defined(featureIds)) {
return {
hasFeatureIds: true,
hasPropertyTable: defined(featureIds.propertyTableId),
};
}
return {
hasFeatureIds: false,
hasPropertyTable: false,
};
}
export default ModelRuntimePrimitive;