PrimitiveLoadPlan.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import Check from "../Core/Check.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import defined from "../Core/defined.js";
  4. import IndexDatatype from "../Core/IndexDatatype.js";
  5. import Buffer from "../Renderer/Buffer.js";
  6. import BufferUsage from "../Renderer/BufferUsage.js";
  7. import AttributeType from "./AttributeType.js";
  8. import ModelComponents from "./ModelComponents.js";
  9. import PrimitiveOutlineGenerator from "./Model/PrimitiveOutlineGenerator.js";
  10. /**
  11. * Simple struct for tracking whether an attribute will be loaded as a buffer
  12. * or typed array after post-processing.
  13. *
  14. * @alias PrimitiveLoadPlan.AttributeLoadPlan
  15. * @constructor
  16. *
  17. * @param {ModelComponents.Attribute} attribute The attribute to be updated
  18. *
  19. * @private
  20. */
  21. function AttributeLoadPlan(attribute) {
  22. //>>includeStart('debug', pragmas.debug);
  23. Check.typeOf.object("attribute", attribute);
  24. //>>includeEnd('debug');
  25. /**
  26. * The attribute to track.
  27. *
  28. * @type {ModelComponents.Attribute}
  29. * @readonly
  30. * @private
  31. */
  32. this.attribute = attribute;
  33. /**
  34. * Whether the attribute will be loaded as a GPU buffer by the time
  35. * {@link PrimitiveLoadPlan#postProcess} is finished.
  36. *
  37. * @type {boolean}
  38. * @private
  39. */
  40. this.loadBuffer = false;
  41. /**
  42. * Whether the attribute will be loaded as a packed typed array by the time
  43. * {@link PrimitiveLoadPlan#postProcess} is finished.
  44. *
  45. * @type {boolean}
  46. * @private
  47. */
  48. this.loadTypedArray = false;
  49. }
  50. /**
  51. * Simple struct for tracking whether an index buffer will be loaded as a buffer
  52. * or typed array after post-processing.
  53. *
  54. * @alias PrimitiveLoadPlan.IndicesLoadPlan
  55. * @constructor
  56. *
  57. * @param {ModelComponents.Indices} indices The indices to be updated
  58. *
  59. * @private
  60. */
  61. function IndicesLoadPlan(indices) {
  62. //>>includeStart('debug', pragmas.debug);
  63. Check.typeOf.object("indices", indices);
  64. //>>includeEnd('debug');
  65. /**
  66. * The indices to track.
  67. *
  68. * @type {ModelComponents.Indices}
  69. * @readonly
  70. * @private
  71. */
  72. this.indices = indices;
  73. /**
  74. * Whether the indices will be loaded as a GPU buffer by the time
  75. * {@link PrimitiveLoadPlan#postProcess} is finished.
  76. *
  77. * @type {boolean}
  78. * @private
  79. */
  80. this.loadBuffer = false;
  81. /**
  82. * Whether the indices will be loaded as a typed array copy of the GPU
  83. * buffer by the time {@link PrimitiveLoadPlan#postProcess} is finished.
  84. *
  85. * @type {boolean}
  86. * @private
  87. */
  88. this.loadTypedArray = false;
  89. }
  90. /**
  91. * Primitives may need post-processing steps after their attributes and indices
  92. * have loaded, such as generating outlines for the CESIUM_primitive_outline glTF
  93. * extension. This object tracks what indices and attributes need to be
  94. * post-processed.
  95. *
  96. * @alias PrimitiveLoadPlan
  97. * @constructor
  98. *
  99. * @param {ModelComponents.Primitive} primitive The primitive to track
  100. *
  101. * @private
  102. */
  103. function PrimitiveLoadPlan(primitive) {
  104. //>>includeStart('debug', pragmas.debug);
  105. Check.typeOf.object("primitive", primitive);
  106. //>>includeEnd('debug');
  107. /**
  108. * The primitive to track.
  109. *
  110. * @type {ModelComponents.Primitive}
  111. * @readonly
  112. * @private
  113. */
  114. this.primitive = primitive;
  115. /**
  116. * A flat list of attributes that need to be post-processed. This includes
  117. * both regular attributes and morph target attributes.
  118. *
  119. * @type {PrimitiveLoadPlan.AttributeLoadPlan[]}
  120. * @private
  121. */
  122. this.attributePlans = [];
  123. /**
  124. * Information about the triangle indices that need to be post-processed,
  125. * if they exist.
  126. *
  127. * @type {PrimitiveLoadPlan.IndicesLoadPlan}
  128. * @private
  129. */
  130. this.indicesPlan = undefined;
  131. /**
  132. * Set this true to indicate that the primitive has the
  133. * CESIUM_primitive_outline extension and needs to be post-processed
  134. *
  135. * @type {boolean}
  136. * @private
  137. */
  138. this.needsOutlines = false;
  139. /**
  140. * The outline edge indices from the CESIUM_primitive_outline extension
  141. *
  142. * @type {number[]}
  143. * @private
  144. */
  145. this.outlineIndices = undefined;
  146. }
  147. /**
  148. * Apply post-processing steps that may modify geometry such as generating
  149. * outline coordinates. If no post-processing steps are needed, this function
  150. * is a no-op.
  151. *
  152. * @param {Context} context The context for generating buffers on the GPU
  153. */
  154. PrimitiveLoadPlan.prototype.postProcess = function (context) {
  155. // Handle CESIUM_primitive_outline. This modifies indices and attributes and
  156. // also generates a new attribute for the outline coordinates. These steps
  157. // are synchronous.
  158. if (this.needsOutlines) {
  159. generateOutlines(this);
  160. generateBuffers(this, context);
  161. }
  162. };
  163. function generateOutlines(loadPlan) {
  164. const primitive = loadPlan.primitive;
  165. const indices = primitive.indices;
  166. const vertexCount = primitive.attributes[0].count;
  167. const generator = new PrimitiveOutlineGenerator({
  168. triangleIndices: indices.typedArray,
  169. outlineIndices: loadPlan.outlineIndices,
  170. originalVertexCount: vertexCount,
  171. });
  172. // The generator modifies/adds indices. In some uncommon cases it may have
  173. // to upgrade to 16- or 32-bit indices so the datatype may change.
  174. indices.typedArray = generator.updatedTriangleIndices;
  175. indices.indexDatatype = IndexDatatype.fromTypedArray(indices.typedArray);
  176. // The outline generator creates a new attribute for the outline coordinates
  177. // that are used with a lookup texture.
  178. const outlineCoordinates = makeOutlineCoordinatesAttribute(
  179. generator.outlineCoordinates
  180. );
  181. const outlineCoordinatesPlan = new AttributeLoadPlan(outlineCoordinates);
  182. outlineCoordinatesPlan.loadBuffer = true;
  183. outlineCoordinatesPlan.loadTypedArray = false;
  184. loadPlan.attributePlans.push(outlineCoordinatesPlan);
  185. primitive.outlineCoordinates = outlineCoordinatesPlan.attribute;
  186. // Some vertices may be copied due to the addition of the new attribute
  187. // which may have multiple values at a vertex depending on the face
  188. const attributePlans = loadPlan.attributePlans;
  189. const attributesLength = loadPlan.attributePlans.length;
  190. for (let i = 0; i < attributesLength; i++) {
  191. const attribute = attributePlans[i].attribute;
  192. attribute.typedArray = generator.updateAttribute(attribute.typedArray);
  193. }
  194. }
  195. function makeOutlineCoordinatesAttribute(outlineCoordinatesTypedArray) {
  196. const attribute = new ModelComponents.Attribute();
  197. attribute.name = "_OUTLINE_COORDINATES";
  198. attribute.typedArray = outlineCoordinatesTypedArray;
  199. attribute.componentDatatype = ComponentDatatype.FLOAT;
  200. attribute.type = AttributeType.VEC3;
  201. attribute.normalized = false;
  202. attribute.count = outlineCoordinatesTypedArray.length / 3;
  203. return attribute;
  204. }
  205. function generateBuffers(loadPlan, context) {
  206. generateAttributeBuffers(loadPlan.attributePlans, context);
  207. if (defined(loadPlan.indicesPlan)) {
  208. generateIndexBuffers(loadPlan.indicesPlan, context);
  209. }
  210. }
  211. function generateAttributeBuffers(attributePlans, context) {
  212. const attributesLength = attributePlans.length;
  213. for (let i = 0; i < attributesLength; i++) {
  214. const attributePlan = attributePlans[i];
  215. const attribute = attributePlan.attribute;
  216. const typedArray = attribute.typedArray;
  217. if (attributePlan.loadBuffer) {
  218. const buffer = Buffer.createVertexBuffer({
  219. typedArray: typedArray,
  220. context: context,
  221. usage: BufferUsage.STATIC_DRAW,
  222. });
  223. buffer.vertexArrayDestroyable = false;
  224. attribute.buffer = buffer;
  225. }
  226. if (!attributePlan.loadTypedArray) {
  227. attribute.typedArray = undefined;
  228. }
  229. }
  230. }
  231. function generateIndexBuffers(indicesPlan, context) {
  232. const indices = indicesPlan.indices;
  233. if (indicesPlan.loadBuffer) {
  234. const buffer = Buffer.createIndexBuffer({
  235. typedArray: indices.typedArray,
  236. context: context,
  237. usage: BufferUsage.STATIC_DRAW,
  238. indexDatatype: indices.indexDatatype,
  239. });
  240. indices.buffer = buffer;
  241. buffer.vertexArrayDestroyable = false;
  242. }
  243. if (!indicesPlan.loadTypedArray) {
  244. indices.typedArray = undefined;
  245. }
  246. }
  247. PrimitiveLoadPlan.AttributeLoadPlan = AttributeLoadPlan;
  248. PrimitiveLoadPlan.IndicesLoadPlan = IndicesLoadPlan;
  249. export default PrimitiveLoadPlan;