DrawCommand.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. import defaultValue from "../Core/defaultValue.js";
  2. import defined from "../Core/defined.js";
  3. import PrimitiveType from "../Core/PrimitiveType.js";
  4. const Flags = {
  5. CULL: 1,
  6. OCCLUDE: 2,
  7. EXECUTE_IN_CLOSEST_FRUSTUM: 4,
  8. DEBUG_SHOW_BOUNDING_VOLUME: 8,
  9. CAST_SHADOWS: 16,
  10. RECEIVE_SHADOWS: 32,
  11. PICK_ONLY: 64,
  12. DEPTH_FOR_TRANSLUCENT_CLASSIFICATION: 128,
  13. };
  14. /**
  15. * Represents a command to the renderer for drawing.
  16. *
  17. * @private
  18. */
  19. function DrawCommand(options) {
  20. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  21. this._boundingVolume = options.boundingVolume;
  22. this._orientedBoundingBox = options.orientedBoundingBox;
  23. this._modelMatrix = options.modelMatrix;
  24. this._primitiveType = defaultValue(
  25. options.primitiveType,
  26. PrimitiveType.TRIANGLES
  27. );
  28. this._vertexArray = options.vertexArray;
  29. this._count = options.count;
  30. this._offset = defaultValue(options.offset, 0);
  31. this._instanceCount = defaultValue(options.instanceCount, 0);
  32. this._shaderProgram = options.shaderProgram;
  33. this._uniformMap = options.uniformMap;
  34. this._renderState = options.renderState;
  35. this._framebuffer = options.framebuffer;
  36. this._pass = options.pass;
  37. this._owner = options.owner;
  38. this._debugOverlappingFrustums = 0;
  39. this._pickId = options.pickId;
  40. // Set initial flags.
  41. this._flags = 0;
  42. this.cull = defaultValue(options.cull, true);
  43. this.occlude = defaultValue(options.occlude, true);
  44. this.executeInClosestFrustum = defaultValue(
  45. options.executeInClosestFrustum,
  46. false
  47. );
  48. this.debugShowBoundingVolume = defaultValue(
  49. options.debugShowBoundingVolume,
  50. false
  51. );
  52. this.castShadows = defaultValue(options.castShadows, false);
  53. this.receiveShadows = defaultValue(options.receiveShadows, false);
  54. this.pickOnly = defaultValue(options.pickOnly, false);
  55. this.depthForTranslucentClassification = defaultValue(
  56. options.depthForTranslucentClassification,
  57. false
  58. );
  59. this.dirty = true;
  60. this.lastDirtyTime = 0;
  61. /**
  62. * @private
  63. */
  64. this.derivedCommands = {};
  65. }
  66. function hasFlag(command, flag) {
  67. return (command._flags & flag) === flag;
  68. }
  69. function setFlag(command, flag, value) {
  70. if (value) {
  71. command._flags |= flag;
  72. } else {
  73. command._flags &= ~flag;
  74. }
  75. }
  76. Object.defineProperties(DrawCommand.prototype, {
  77. /**
  78. * The bounding volume of the geometry in world space. This is used for culling and frustum selection.
  79. * <p>
  80. * For best rendering performance, use the tightest possible bounding volume. Although
  81. * <code>undefined</code> is allowed, always try to provide a bounding volume to
  82. * allow the tightest possible near and far planes to be computed for the scene, and
  83. * minimize the number of frustums needed.
  84. * </p>
  85. *
  86. * @memberof DrawCommand.prototype
  87. * @type {object}
  88. * @default undefined
  89. *
  90. * @see DrawCommand#debugShowBoundingVolume
  91. */
  92. boundingVolume: {
  93. get: function () {
  94. return this._boundingVolume;
  95. },
  96. set: function (value) {
  97. if (this._boundingVolume !== value) {
  98. this._boundingVolume = value;
  99. this.dirty = true;
  100. }
  101. },
  102. },
  103. /**
  104. * The oriented bounding box of the geometry in world space. If this is defined, it is used instead of
  105. * {@link DrawCommand#boundingVolume} for plane intersection testing.
  106. *
  107. * @memberof DrawCommand.prototype
  108. * @type {OrientedBoundingBox}
  109. * @default undefined
  110. *
  111. * @see DrawCommand#debugShowBoundingVolume
  112. */
  113. orientedBoundingBox: {
  114. get: function () {
  115. return this._orientedBoundingBox;
  116. },
  117. set: function (value) {
  118. if (this._orientedBoundingBox !== value) {
  119. this._orientedBoundingBox = value;
  120. this.dirty = true;
  121. }
  122. },
  123. },
  124. /**
  125. * When <code>true</code>, the renderer frustum and horizon culls the command based on its {@link DrawCommand#boundingVolume}.
  126. * If the command was already culled, set this to <code>false</code> for a performance improvement.
  127. *
  128. * @memberof DrawCommand.prototype
  129. * @type {boolean}
  130. * @default true
  131. */
  132. cull: {
  133. get: function () {
  134. return hasFlag(this, Flags.CULL);
  135. },
  136. set: function (value) {
  137. if (hasFlag(this, Flags.CULL) !== value) {
  138. setFlag(this, Flags.CULL, value);
  139. this.dirty = true;
  140. }
  141. },
  142. },
  143. /**
  144. * When <code>true</code>, the horizon culls the command based on its {@link DrawCommand#boundingVolume}.
  145. * {@link DrawCommand#cull} must also be <code>true</code> in order for the command to be culled.
  146. *
  147. * @memberof DrawCommand.prototype
  148. * @type {boolean}
  149. * @default true
  150. */
  151. occlude: {
  152. get: function () {
  153. return hasFlag(this, Flags.OCCLUDE);
  154. },
  155. set: function (value) {
  156. if (hasFlag(this, Flags.OCCLUDE) !== value) {
  157. setFlag(this, Flags.OCCLUDE, value);
  158. this.dirty = true;
  159. }
  160. },
  161. },
  162. /**
  163. * The transformation from the geometry in model space to world space.
  164. * <p>
  165. * When <code>undefined</code>, the geometry is assumed to be defined in world space.
  166. * </p>
  167. *
  168. * @memberof DrawCommand.prototype
  169. * @type {Matrix4}
  170. * @default undefined
  171. */
  172. modelMatrix: {
  173. get: function () {
  174. return this._modelMatrix;
  175. },
  176. set: function (value) {
  177. if (this._modelMatrix !== value) {
  178. this._modelMatrix = value;
  179. this.dirty = true;
  180. }
  181. },
  182. },
  183. /**
  184. * The type of geometry in the vertex array.
  185. *
  186. * @memberof DrawCommand.prototype
  187. * @type {PrimitiveType}
  188. * @default PrimitiveType.TRIANGLES
  189. */
  190. primitiveType: {
  191. get: function () {
  192. return this._primitiveType;
  193. },
  194. set: function (value) {
  195. if (this._primitiveType !== value) {
  196. this._primitiveType = value;
  197. this.dirty = true;
  198. }
  199. },
  200. },
  201. /**
  202. * The vertex array.
  203. *
  204. * @memberof DrawCommand.prototype
  205. * @type {VertexArray}
  206. * @default undefined
  207. */
  208. vertexArray: {
  209. get: function () {
  210. return this._vertexArray;
  211. },
  212. set: function (value) {
  213. if (this._vertexArray !== value) {
  214. this._vertexArray = value;
  215. this.dirty = true;
  216. }
  217. },
  218. },
  219. /**
  220. * The number of vertices to draw in the vertex array.
  221. *
  222. * @memberof DrawCommand.prototype
  223. * @type {number}
  224. * @default undefined
  225. */
  226. count: {
  227. get: function () {
  228. return this._count;
  229. },
  230. set: function (value) {
  231. if (this._count !== value) {
  232. this._count = value;
  233. this.dirty = true;
  234. }
  235. },
  236. },
  237. /**
  238. * The offset to start drawing in the vertex array.
  239. *
  240. * @memberof DrawCommand.prototype
  241. * @type {number}
  242. * @default 0
  243. */
  244. offset: {
  245. get: function () {
  246. return this._offset;
  247. },
  248. set: function (value) {
  249. if (this._offset !== value) {
  250. this._offset = value;
  251. this.dirty = true;
  252. }
  253. },
  254. },
  255. /**
  256. * The number of instances to draw.
  257. *
  258. * @memberof DrawCommand.prototype
  259. * @type {number}
  260. * @default 0
  261. */
  262. instanceCount: {
  263. get: function () {
  264. return this._instanceCount;
  265. },
  266. set: function (value) {
  267. if (this._instanceCount !== value) {
  268. this._instanceCount = value;
  269. this.dirty = true;
  270. }
  271. },
  272. },
  273. /**
  274. * The shader program to apply.
  275. *
  276. * @memberof DrawCommand.prototype
  277. * @type {ShaderProgram}
  278. * @default undefined
  279. */
  280. shaderProgram: {
  281. get: function () {
  282. return this._shaderProgram;
  283. },
  284. set: function (value) {
  285. if (this._shaderProgram !== value) {
  286. this._shaderProgram = value;
  287. this.dirty = true;
  288. }
  289. },
  290. },
  291. /**
  292. * Whether this command should cast shadows when shadowing is enabled.
  293. *
  294. * @memberof DrawCommand.prototype
  295. * @type {boolean}
  296. * @default false
  297. */
  298. castShadows: {
  299. get: function () {
  300. return hasFlag(this, Flags.CAST_SHADOWS);
  301. },
  302. set: function (value) {
  303. if (hasFlag(this, Flags.CAST_SHADOWS) !== value) {
  304. setFlag(this, Flags.CAST_SHADOWS, value);
  305. this.dirty = true;
  306. }
  307. },
  308. },
  309. /**
  310. * Whether this command should receive shadows when shadowing is enabled.
  311. *
  312. * @memberof DrawCommand.prototype
  313. * @type {boolean}
  314. * @default false
  315. */
  316. receiveShadows: {
  317. get: function () {
  318. return hasFlag(this, Flags.RECEIVE_SHADOWS);
  319. },
  320. set: function (value) {
  321. if (hasFlag(this, Flags.RECEIVE_SHADOWS) !== value) {
  322. setFlag(this, Flags.RECEIVE_SHADOWS, value);
  323. this.dirty = true;
  324. }
  325. },
  326. },
  327. /**
  328. * An object with functions whose names match the uniforms in the shader program
  329. * and return values to set those uniforms.
  330. *
  331. * @memberof DrawCommand.prototype
  332. * @type {object}
  333. * @default undefined
  334. */
  335. uniformMap: {
  336. get: function () {
  337. return this._uniformMap;
  338. },
  339. set: function (value) {
  340. if (this._uniformMap !== value) {
  341. this._uniformMap = value;
  342. this.dirty = true;
  343. }
  344. },
  345. },
  346. /**
  347. * The render state.
  348. *
  349. * @memberof DrawCommand.prototype
  350. * @type {RenderState}
  351. * @default undefined
  352. */
  353. renderState: {
  354. get: function () {
  355. return this._renderState;
  356. },
  357. set: function (value) {
  358. if (this._renderState !== value) {
  359. this._renderState = value;
  360. this.dirty = true;
  361. }
  362. },
  363. },
  364. /**
  365. * The framebuffer to draw to.
  366. *
  367. * @memberof DrawCommand.prototype
  368. * @type {Framebuffer}
  369. * @default undefined
  370. */
  371. framebuffer: {
  372. get: function () {
  373. return this._framebuffer;
  374. },
  375. set: function (value) {
  376. if (this._framebuffer !== value) {
  377. this._framebuffer = value;
  378. this.dirty = true;
  379. }
  380. },
  381. },
  382. /**
  383. * The pass when to render.
  384. *
  385. * @memberof DrawCommand.prototype
  386. * @type {Pass}
  387. * @default undefined
  388. */
  389. pass: {
  390. get: function () {
  391. return this._pass;
  392. },
  393. set: function (value) {
  394. if (this._pass !== value) {
  395. this._pass = value;
  396. this.dirty = true;
  397. }
  398. },
  399. },
  400. /**
  401. * Specifies if this command is only to be executed in the frustum closest
  402. * to the eye containing the bounding volume. Defaults to <code>false</code>.
  403. *
  404. * @memberof DrawCommand.prototype
  405. * @type {boolean}
  406. * @default false
  407. */
  408. executeInClosestFrustum: {
  409. get: function () {
  410. return hasFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM);
  411. },
  412. set: function (value) {
  413. if (hasFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM) !== value) {
  414. setFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM, value);
  415. this.dirty = true;
  416. }
  417. },
  418. },
  419. /**
  420. * The object who created this command. This is useful for debugging command
  421. * execution; it allows us to see who created a command when we only have a
  422. * reference to the command, and can be used to selectively execute commands
  423. * with {@link Scene#debugCommandFilter}.
  424. *
  425. * @memberof DrawCommand.prototype
  426. * @type {object}
  427. * @default undefined
  428. *
  429. * @see Scene#debugCommandFilter
  430. */
  431. owner: {
  432. get: function () {
  433. return this._owner;
  434. },
  435. set: function (value) {
  436. if (this._owner !== value) {
  437. this._owner = value;
  438. this.dirty = true;
  439. }
  440. },
  441. },
  442. /**
  443. * This property is for debugging only; it is not for production use nor is it optimized.
  444. * <p>
  445. * Draws the {@link DrawCommand#boundingVolume} for this command, assuming it is a sphere, when the command executes.
  446. * </p>
  447. *
  448. * @memberof DrawCommand.prototype
  449. * @type {boolean}
  450. * @default false
  451. *
  452. * @see DrawCommand#boundingVolume
  453. */
  454. debugShowBoundingVolume: {
  455. get: function () {
  456. return hasFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME);
  457. },
  458. set: function (value) {
  459. if (hasFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME) !== value) {
  460. setFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME, value);
  461. this.dirty = true;
  462. }
  463. },
  464. },
  465. /**
  466. * Used to implement Scene.debugShowFrustums.
  467. * @private
  468. */
  469. debugOverlappingFrustums: {
  470. get: function () {
  471. return this._debugOverlappingFrustums;
  472. },
  473. set: function (value) {
  474. if (this._debugOverlappingFrustums !== value) {
  475. this._debugOverlappingFrustums = value;
  476. this.dirty = true;
  477. }
  478. },
  479. },
  480. /**
  481. * A GLSL string that will evaluate to a pick id. When <code>undefined</code>, the command will only draw depth
  482. * during the pick pass.
  483. *
  484. * @memberof DrawCommand.prototype
  485. * @type {string}
  486. * @default undefined
  487. */
  488. pickId: {
  489. get: function () {
  490. return this._pickId;
  491. },
  492. set: function (value) {
  493. if (this._pickId !== value) {
  494. this._pickId = value;
  495. this.dirty = true;
  496. }
  497. },
  498. },
  499. /**
  500. * Whether this command should be executed in the pick pass only.
  501. *
  502. * @memberof DrawCommand.prototype
  503. * @type {boolean}
  504. * @default false
  505. */
  506. pickOnly: {
  507. get: function () {
  508. return hasFlag(this, Flags.PICK_ONLY);
  509. },
  510. set: function (value) {
  511. if (hasFlag(this, Flags.PICK_ONLY) !== value) {
  512. setFlag(this, Flags.PICK_ONLY, value);
  513. this.dirty = true;
  514. }
  515. },
  516. },
  517. /**
  518. * Whether this command should be derived to draw depth for classification of translucent primitives.
  519. *
  520. * @memberof DrawCommand.prototype
  521. * @type {boolean}
  522. * @default false
  523. */
  524. depthForTranslucentClassification: {
  525. get: function () {
  526. return hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION);
  527. },
  528. set: function (value) {
  529. if (hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION) !== value) {
  530. setFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION, value);
  531. this.dirty = true;
  532. }
  533. },
  534. },
  535. });
  536. /**
  537. * @private
  538. */
  539. DrawCommand.shallowClone = function (command, result) {
  540. if (!defined(command)) {
  541. return undefined;
  542. }
  543. if (!defined(result)) {
  544. result = new DrawCommand();
  545. }
  546. result._boundingVolume = command._boundingVolume;
  547. result._orientedBoundingBox = command._orientedBoundingBox;
  548. result._modelMatrix = command._modelMatrix;
  549. result._primitiveType = command._primitiveType;
  550. result._vertexArray = command._vertexArray;
  551. result._count = command._count;
  552. result._offset = command._offset;
  553. result._instanceCount = command._instanceCount;
  554. result._shaderProgram = command._shaderProgram;
  555. result._uniformMap = command._uniformMap;
  556. result._renderState = command._renderState;
  557. result._framebuffer = command._framebuffer;
  558. result._pass = command._pass;
  559. result._owner = command._owner;
  560. result._debugOverlappingFrustums = command._debugOverlappingFrustums;
  561. result._pickId = command._pickId;
  562. result._flags = command._flags;
  563. result.dirty = true;
  564. result.lastDirtyTime = 0;
  565. return result;
  566. };
  567. /**
  568. * Executes the draw command.
  569. *
  570. * @param {Context} context The renderer context in which to draw.
  571. * @param {PassState} [passState] The state for the current render pass.
  572. */
  573. DrawCommand.prototype.execute = function (context, passState) {
  574. context.draw(this, passState);
  575. };
  576. export default DrawCommand;