ModelRuntimePrimitive.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import Check from "../../Core/Check.js";
  2. import defaultValue from "../../Core/defaultValue.js";
  3. import defined from "../../Core/defined.js";
  4. import PrimitiveType from "../../Core/PrimitiveType.js";
  5. import SceneMode from "../SceneMode.js";
  6. import AlphaPipelineStage from "./AlphaPipelineStage.js";
  7. import BatchTexturePipelineStage from "./BatchTexturePipelineStage.js";
  8. import ClassificationPipelineStage from "./ClassificationPipelineStage.js";
  9. import CPUStylingPipelineStage from "./CPUStylingPipelineStage.js";
  10. import CustomShaderMode from "./CustomShaderMode.js";
  11. import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js";
  12. import DequantizationPipelineStage from "./DequantizationPipelineStage.js";
  13. import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
  14. import GeometryPipelineStage from "./GeometryPipelineStage.js";
  15. import LightingPipelineStage from "./LightingPipelineStage.js";
  16. import MaterialPipelineStage from "./MaterialPipelineStage.js";
  17. import MetadataPipelineStage from "./MetadataPipelineStage.js";
  18. import ModelUtility from "./ModelUtility.js";
  19. import MorphTargetsPipelineStage from "./MorphTargetsPipelineStage.js";
  20. import PickingPipelineStage from "./PickingPipelineStage.js";
  21. import PointCloudStylingPipelineStage from "./PointCloudStylingPipelineStage.js";
  22. import PrimitiveOutlinePipelineStage from "./PrimitiveOutlinePipelineStage.js";
  23. import PrimitiveStatisticsPipelineStage from "./PrimitiveStatisticsPipelineStage.js";
  24. import SceneMode2DPipelineStage from "./SceneMode2DPipelineStage.js";
  25. import SelectedFeatureIdPipelineStage from "./SelectedFeatureIdPipelineStage.js";
  26. import SkinningPipelineStage from "./SkinningPipelineStage.js";
  27. import WireframePipelineStage from "./WireframePipelineStage.js";
  28. /**
  29. * In memory representation of a single primitive, that is, a primitive
  30. * and its corresponding mesh.
  31. *
  32. * @param {object} options An object containing the following options:
  33. * @param {ModelComponents.Primitive} options.primitive The primitive component.
  34. * @param {ModelComponents.Node} options.node The node that this primitive belongs to.
  35. * @param {Model} options.model The {@link Model} this primitive belongs to.
  36. *
  37. * @alias ModelRuntimePrimitive
  38. * @constructor
  39. *
  40. * @private
  41. */
  42. function ModelRuntimePrimitive(options) {
  43. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  44. const primitive = options.primitive;
  45. const node = options.node;
  46. const model = options.model;
  47. //>>includeStart('debug', pragmas.debug);
  48. Check.typeOf.object("options.primitive", primitive);
  49. Check.typeOf.object("options.node", node);
  50. Check.typeOf.object("options.model", model);
  51. //>>includeEnd('debug');
  52. /**
  53. * The primitive component associated with this primitive.
  54. *
  55. * @type {ModelComponents.Primitive}
  56. *
  57. * @private
  58. */
  59. this.primitive = primitive;
  60. /**
  61. * A reference to the node this primitive belongs to.
  62. *
  63. * @type {ModelComponents.Node}
  64. *
  65. * @private
  66. */
  67. this.node = node;
  68. /**
  69. * A reference to the model
  70. *
  71. * @type {Model}
  72. *
  73. * @private
  74. */
  75. this.model = model;
  76. /**
  77. * Pipeline stages to apply to this primitive. This
  78. * is an array of classes, each with a static method called
  79. * <code>process()</code>
  80. *
  81. * @type {Object[]}
  82. * @readonly
  83. *
  84. * @private
  85. */
  86. this.pipelineStages = [];
  87. /**
  88. * The generated {@link ModelDrawCommand} or {@link ClassificationModelDrawCommand}
  89. * associated with this primitive.
  90. *
  91. * @type {ModelDrawCommand|ClassificationModelDrawCommand}
  92. *
  93. * @private
  94. */
  95. this.drawCommand = undefined;
  96. /**
  97. * The bounding sphere of this primitive in object-space.
  98. *
  99. * @type {BoundingSphere}
  100. *
  101. * @private
  102. */
  103. this.boundingSphere = undefined;
  104. /**
  105. * The bounding sphere of this primitive in 2D world space.
  106. *
  107. * @type {BoundingSphere}
  108. *
  109. * @private
  110. */
  111. this.boundingSphere2D = undefined;
  112. /**
  113. * A buffer containing the primitive's positions projected to 2D world
  114. * coordinates. This is generated by SceneMode2DPipelineStage and used for
  115. * rendering in 2D / CV mode. The memory is managed by Model; this is just
  116. * a reference.
  117. *
  118. * @type {Buffer}
  119. *
  120. * @private
  121. */
  122. this.positionBuffer2D = undefined;
  123. /**
  124. * An array containing the lengths of the vertex batches for classification.
  125. * Vertices with the same feature ID are batched together, and each batch is
  126. * drawn with a different draw command in order to properly classify other
  127. * assets.
  128. * <p>
  129. * This is generated by ClassificationPipelineStage. The memory is managed by
  130. * Model; this is just a reference.
  131. * </p>
  132. *
  133. * @type {number[]}
  134. *
  135. * @private
  136. */
  137. this.batchLengths = undefined;
  138. /**
  139. * An array containing the offsets of the vertex batches for classification.
  140. * Vertices with the same feature ID are batched together, and each batch is
  141. * drawn with a different draw command in order to properly classify other
  142. * assets.
  143. * <p>
  144. * This is generated by ClassificationPipelineStage. The memory is managed by
  145. * Model; this is just a reference.
  146. * </p>
  147. *
  148. * @type {number[]}
  149. *
  150. * @private
  151. */
  152. this.batchOffsets = undefined;
  153. /**
  154. * Update stages to apply to this primitive.
  155. *
  156. * @type {Object[]}
  157. * @readonly
  158. *
  159. * @private
  160. */
  161. this.updateStages = [];
  162. }
  163. /**
  164. * Configure the primitive pipeline stages. If the pipeline needs to be re-run,
  165. * call this method again to ensure the correct sequence of pipeline stages are
  166. * used.
  167. *
  168. * @param {FrameState} frameState The frame state.
  169. *
  170. * @private
  171. */
  172. ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
  173. const pipelineStages = this.pipelineStages;
  174. pipelineStages.length = 0;
  175. const primitive = this.primitive;
  176. const node = this.node;
  177. const model = this.model;
  178. const customShader = model.customShader;
  179. const style = model.style;
  180. const useWebgl2 = frameState.context.webgl2;
  181. const mode = frameState.mode;
  182. const use2D =
  183. mode !== SceneMode.SCENE3D && !frameState.scene3DOnly && model._projectTo2D;
  184. const hasMorphTargets =
  185. defined(primitive.morphTargets) && primitive.morphTargets.length > 0;
  186. const hasSkinning = defined(node.skin);
  187. const hasCustomShader = defined(customShader);
  188. const hasCustomFragmentShader =
  189. hasCustomShader && defined(customShader.fragmentShaderText);
  190. const materialsEnabled =
  191. !hasCustomFragmentShader ||
  192. customShader.mode !== CustomShaderMode.REPLACE_MATERIAL;
  193. const hasQuantization = ModelUtility.hasQuantizedAttributes(
  194. primitive.attributes
  195. );
  196. const generateWireframeIndices =
  197. model.debugWireframe &&
  198. PrimitiveType.isTriangles(primitive.primitiveType) &&
  199. // Generating index buffers for wireframes is always possible in WebGL2.
  200. // However, this will only work in WebGL1 if the model was constructed with
  201. // enableDebugWireframe set to true.
  202. (model._enableDebugWireframe || useWebgl2);
  203. const pointCloudShading = model.pointCloudShading;
  204. const hasAttenuation =
  205. defined(pointCloudShading) && pointCloudShading.attenuation;
  206. const hasPointCloudBackFaceCulling =
  207. defined(pointCloudShading) && pointCloudShading.backFaceCulling;
  208. const hasPointCloudStyle =
  209. primitive.primitiveType === PrimitiveType.POINTS &&
  210. (defined(style) || hasAttenuation || hasPointCloudBackFaceCulling);
  211. const hasOutlines =
  212. model._enableShowOutline && defined(primitive.outlineCoordinates);
  213. const featureIdFlags = inspectFeatureIds(model, node, primitive);
  214. const hasClassification = defined(model.classificationType);
  215. // Start of pipeline -----------------------------------------------------
  216. if (use2D) {
  217. pipelineStages.push(SceneMode2DPipelineStage);
  218. }
  219. pipelineStages.push(GeometryPipelineStage);
  220. if (generateWireframeIndices) {
  221. pipelineStages.push(WireframePipelineStage);
  222. }
  223. if (hasClassification) {
  224. pipelineStages.push(ClassificationPipelineStage);
  225. }
  226. if (hasMorphTargets) {
  227. pipelineStages.push(MorphTargetsPipelineStage);
  228. }
  229. if (hasSkinning) {
  230. pipelineStages.push(SkinningPipelineStage);
  231. }
  232. if (hasPointCloudStyle) {
  233. pipelineStages.push(PointCloudStylingPipelineStage);
  234. }
  235. if (hasQuantization) {
  236. pipelineStages.push(DequantizationPipelineStage);
  237. }
  238. if (materialsEnabled) {
  239. pipelineStages.push(MaterialPipelineStage);
  240. }
  241. // These stages are always run to ensure structs
  242. // are declared to avoid compilation errors.
  243. pipelineStages.push(FeatureIdPipelineStage);
  244. pipelineStages.push(MetadataPipelineStage);
  245. if (featureIdFlags.hasPropertyTable) {
  246. pipelineStages.push(SelectedFeatureIdPipelineStage);
  247. pipelineStages.push(BatchTexturePipelineStage);
  248. pipelineStages.push(CPUStylingPipelineStage);
  249. }
  250. if (hasCustomShader) {
  251. pipelineStages.push(CustomShaderPipelineStage);
  252. }
  253. pipelineStages.push(LightingPipelineStage);
  254. if (model.allowPicking) {
  255. pipelineStages.push(PickingPipelineStage);
  256. }
  257. if (hasOutlines) {
  258. pipelineStages.push(PrimitiveOutlinePipelineStage);
  259. }
  260. pipelineStages.push(AlphaPipelineStage);
  261. pipelineStages.push(PrimitiveStatisticsPipelineStage);
  262. return;
  263. };
  264. function inspectFeatureIds(model, node, primitive) {
  265. let featureIds;
  266. // Check instances first, as this is the most specific type of
  267. // feature ID
  268. if (defined(node.instances)) {
  269. featureIds = ModelUtility.getFeatureIdsByLabel(
  270. node.instances.featureIds,
  271. model.instanceFeatureIdLabel
  272. );
  273. if (defined(featureIds)) {
  274. return {
  275. hasFeatureIds: true,
  276. hasPropertyTable: defined(featureIds.propertyTableId),
  277. };
  278. }
  279. }
  280. featureIds = ModelUtility.getFeatureIdsByLabel(
  281. primitive.featureIds,
  282. model.featureIdLabel
  283. );
  284. if (defined(featureIds)) {
  285. return {
  286. hasFeatureIds: true,
  287. hasPropertyTable: defined(featureIds.propertyTableId),
  288. };
  289. }
  290. return {
  291. hasFeatureIds: false,
  292. hasPropertyTable: false,
  293. };
  294. }
  295. export default ModelRuntimePrimitive;