| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- // @ts-check
-
- import Frozen from "../Core/Frozen.js";
- import defined from "../Core/defined.js";
- import PrimitiveType from "../Core/PrimitiveType.js";
-
- /** @import Context from "./Context.js"; */
- /** @import Framebuffer from "./Framebuffer.js"; */
- /** @import Matrix4 from "../Core/Matrix4.js"; */
- /** @import OrientedBoundingBox from "../Core/OrientedBoundingBox.js"; */
- /** @import Pass from "./Pass.js"; */
- /** @import PassState from "./PassState.js"; */
- /** @import PickedMetadataInfo from "../Scene/PickedMetadataInfo.js"; */
- /** @import RenderState from "./RenderState.js"; */
- /** @import ShaderProgram from "./ShaderProgram.js"; */
- /** @import VertexArray from "./VertexArray.js"; */
-
- /**
- * @enum {number}
- * @ignore
- */
- const Flags = {
- CULL: 1,
- OCCLUDE: 2,
- EXECUTE_IN_CLOSEST_FRUSTUM: 4,
- DEBUG_SHOW_BOUNDING_VOLUME: 8,
- CAST_SHADOWS: 16,
- RECEIVE_SHADOWS: 32,
- PICK_ONLY: 64,
- DEPTH_FOR_TRANSLUCENT_CLASSIFICATION: 128,
- };
-
- /**
- * @typedef {object} DrawCommandOptions
- * @property {object} [boundingVolume]
- * @property {OrientedBoundingBox} [orientedBoundingBox]
- * @property {Matrix4} [modelMatrix]
- * @property {PrimitiveType} [primitiveType=PrimitiveType.TRIANGLES]
- * @property {VertexArray} [vertexArray]
- * @property {number} [count]
- * @property {number} [offset]
- * @property {number} [instanceCount]
- * @property {ShaderProgram} [shaderProgram]
- * @property {object} [uniformMap]
- * @property {RenderState} [renderState]
- * @property {Framebuffer} [framebuffer]
- * @property {Pass} [pass]
- * @property {object} [owner]
- * @property {string} [pickId]
- * @property {boolean} [pickMetadataAllowed=false]
- * @property {boolean} [cull=true]
- * @property {boolean} [occlude=true]
- * @property {boolean} [executeInClosestFrustum=false]
- * @property {boolean} [debugShowBoundingVolume=false]
- * @property {boolean} [castShadows=false]
- * @property {boolean} [receiveShadows=false]
- * @property {boolean} [pickOnly=false]
- * @property {boolean} [depthForTranslucentClassification=false]
- *
- * @ignore
- */
-
- /**
- * Represents a command to the renderer for drawing.
- *
- * @private
- */
- class DrawCommand {
- /**
- * @param {DrawCommandOptions} [options]
- */
- constructor(options = Frozen.EMPTY_OBJECT) {
- /** @private */
- this._boundingVolume = options.boundingVolume;
- /** @private */
- this._orientedBoundingBox = options.orientedBoundingBox;
- /** @private */
- this._modelMatrix = options.modelMatrix;
- /** @private */
- this._primitiveType = options.primitiveType ?? PrimitiveType.TRIANGLES;
- /** @private */
- this._vertexArray = options.vertexArray;
- /** @private */
- this._count = options.count;
- /** @private */
- this._offset = options.offset ?? 0;
- /** @private */
- this._instanceCount = options.instanceCount ?? 0;
- /** @private */
- this._shaderProgram = options.shaderProgram;
- /** @private */
- this._uniformMap = options.uniformMap;
- /** @private */
- this._renderState = options.renderState;
- /** @private */
- this._framebuffer = options.framebuffer;
- /** @private */
- this._pass = options.pass;
- /** @private */
- this._owner = options.owner;
- /** @private */
- this._debugOverlappingFrustums = 0;
- /** @private */
- this._pickId = options.pickId;
- /** @private */
- this._pickMetadataAllowed = options.pickMetadataAllowed === true;
- /**
- * @type {PickedMetadataInfo|undefined}
- * @private
- */
- this._pickedMetadataInfo = undefined;
-
- // Set initial flags.
- this._flags = 0;
- this.cull = options.cull ?? true;
- this.occlude = options.occlude ?? true;
- this.executeInClosestFrustum = options.executeInClosestFrustum ?? false;
- this.debugShowBoundingVolume = options.debugShowBoundingVolume ?? false;
- this.castShadows = options.castShadows ?? false;
- this.receiveShadows = options.receiveShadows ?? false;
- this.pickOnly = options.pickOnly ?? false;
- this.depthForTranslucentClassification =
- options.depthForTranslucentClassification ?? false;
-
- this.dirty = true;
- this.lastDirtyTime = 0;
-
- /**
- * @private
- */
- this.derivedCommands = {};
- }
-
- /**
- * The bounding volume of the geometry in world space. This is used for culling and frustum selection.
- * <p>
- * For best rendering performance, use the tightest possible bounding volume. Although
- * <code>undefined</code> is allowed, always try to provide a bounding volume to
- * allow the tightest possible near and far planes to be computed for the scene, and
- * minimize the number of frustums needed.
- * </p>
- *
- * @type {object}
- * @default undefined
- *
- * @see DrawCommand#debugShowBoundingVolume
- */
- get boundingVolume() {
- return this._boundingVolume;
- }
-
- set boundingVolume(value) {
- if (this._boundingVolume !== value) {
- this._boundingVolume = value;
- this.dirty = true;
- }
- }
-
- /**
- * The oriented bounding box of the geometry in world space. If this is defined, it is used instead of
- * {@link DrawCommand#boundingVolume} for plane intersection testing.
- *
- * @type {OrientedBoundingBox}
- * @default undefined
- *
- * @see DrawCommand#debugShowBoundingVolume
- */
- get orientedBoundingBox() {
- return this._orientedBoundingBox;
- }
-
- set orientedBoundingBox(value) {
- if (this._orientedBoundingBox !== value) {
- this._orientedBoundingBox = value;
- this.dirty = true;
- }
- }
-
- /**
- * When <code>true</code>, the renderer frustum and horizon culls the command based on its {@link DrawCommand#boundingVolume}.
- * If the command was already culled, set this to <code>false</code> for a performance improvement.
- *
- * @type {boolean}
- * @default true
- */
- get cull() {
- return hasFlag(this, Flags.CULL);
- }
-
- set cull(value) {
- if (hasFlag(this, Flags.CULL) !== value) {
- setFlag(this, Flags.CULL, value);
- this.dirty = true;
- }
- }
-
- /**
- * When <code>true</code>, the horizon culls the command based on its {@link DrawCommand#boundingVolume}.
- * {@link DrawCommand#cull} must also be <code>true</code> in order for the command to be culled.
- *
- * @type {boolean}
- * @default true
- */
- get occlude() {
- return hasFlag(this, Flags.OCCLUDE);
- }
-
- set occlude(value) {
- if (hasFlag(this, Flags.OCCLUDE) !== value) {
- setFlag(this, Flags.OCCLUDE, value);
- this.dirty = true;
- }
- }
-
- /**
- * The transformation from the geometry in model space to world space.
- * <p>
- * When <code>undefined</code>, the geometry is assumed to be defined in world space.
- * </p>
- *
- * @type {Matrix4}
- * @default undefined
- */
- get modelMatrix() {
- return this._modelMatrix;
- }
-
- set modelMatrix(value) {
- if (this._modelMatrix !== value) {
- this._modelMatrix = value;
- this.dirty = true;
- }
- }
-
- /**
- * The type of geometry in the vertex array.
- *
- * @type {PrimitiveType}
- * @default PrimitiveType.TRIANGLES
- */
- get primitiveType() {
- return this._primitiveType;
- }
-
- set primitiveType(value) {
- if (this._primitiveType !== value) {
- this._primitiveType = value;
- this.dirty = true;
- }
- }
-
- /**
- * The vertex array.
- *
- * @type {VertexArray}
- * @default undefined
- */
- get vertexArray() {
- return this._vertexArray;
- }
-
- set vertexArray(value) {
- if (this._vertexArray !== value) {
- this._vertexArray = value;
- this.dirty = true;
- }
- }
-
- /**
- * The number of vertices to draw in the vertex array.
- *
- * @type {number}
- * @default undefined
- */
- get count() {
- return this._count;
- }
-
- set count(value) {
- if (this._count !== value) {
- this._count = value;
- this.dirty = true;
- }
- }
-
- /**
- * The offset to start drawing in the vertex array.
- *
- * @type {number}
- * @default 0
- */
- get offset() {
- return this._offset;
- }
-
- set offset(value) {
- if (this._offset !== value) {
- this._offset = value;
- this.dirty = true;
- }
- }
-
- /**
- * The number of instances to draw.
- *
- * @type {number}
- * @default 0
- */
- get instanceCount() {
- return this._instanceCount;
- }
-
- set instanceCount(value) {
- if (this._instanceCount !== value) {
- this._instanceCount = value;
- this.dirty = true;
- }
- }
-
- /**
- * The shader program to apply.
- *
- * @type {ShaderProgram}
- * @default undefined
- */
- get shaderProgram() {
- return this._shaderProgram;
- }
-
- set shaderProgram(value) {
- if (this._shaderProgram !== value) {
- this._shaderProgram = value;
- this.dirty = true;
- }
- }
-
- /**
- * Whether this command should cast shadows when shadowing is enabled.
- *
- * @type {boolean}
- * @default false
- */
- get castShadows() {
- return hasFlag(this, Flags.CAST_SHADOWS);
- }
-
- set castShadows(value) {
- if (hasFlag(this, Flags.CAST_SHADOWS) !== value) {
- setFlag(this, Flags.CAST_SHADOWS, value);
- this.dirty = true;
- }
- }
-
- /**
- * Whether this command should receive shadows when shadowing is enabled.
- *
- * @type {boolean}
- * @default false
- */
- get receiveShadows() {
- return hasFlag(this, Flags.RECEIVE_SHADOWS);
- }
-
- set receiveShadows(value) {
- if (hasFlag(this, Flags.RECEIVE_SHADOWS) !== value) {
- setFlag(this, Flags.RECEIVE_SHADOWS, value);
- this.dirty = true;
- }
- }
-
- /**
- * An object with functions whose names match the uniforms in the shader program
- * and return values to set those uniforms.
- *
- * @type {object}
- * @default undefined
- */
- get uniformMap() {
- return this._uniformMap;
- }
-
- set uniformMap(value) {
- if (this._uniformMap !== value) {
- this._uniformMap = value;
- this.dirty = true;
- }
- }
-
- /**
- * The render state.
- *
- * @type {RenderState}
- * @default undefined
- */
- get renderState() {
- return this._renderState;
- }
-
- set renderState(value) {
- if (this._renderState !== value) {
- this._renderState = value;
- this.dirty = true;
- }
- }
-
- /**
- * The framebuffer to draw to.
- *
- * @type {Framebuffer}
- * @default undefined
- */
- get framebuffer() {
- return this._framebuffer;
- }
-
- set framebuffer(value) {
- if (this._framebuffer !== value) {
- this._framebuffer = value;
- this.dirty = true;
- }
- }
-
- /**
- * The pass when to render.
- *
- * @type {Pass}
- * @default undefined
- */
- get pass() {
- return this._pass;
- }
-
- set pass(value) {
- if (this._pass !== value) {
- this._pass = value;
- this.dirty = true;
- }
- }
-
- /**
- * Specifies if this command is only to be executed in the frustum closest
- * to the eye containing the bounding volume. Defaults to <code>false</code>.
- *
- * @type {boolean}
- * @default false
- */
- get executeInClosestFrustum() {
- return hasFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM);
- }
-
- set executeInClosestFrustum(value) {
- if (hasFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM) !== value) {
- setFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM, value);
- this.dirty = true;
- }
- }
-
- /**
- * The object who created this command. This is useful for debugging command
- * execution; it allows us to see who created a command when we only have a
- * reference to the command, and can be used to selectively execute commands
- * with {@link Scene#debugCommandFilter}.
- *
- * @type {object}
- * @default undefined
- *
- * @see Scene#debugCommandFilter
- */
- get owner() {
- return this._owner;
- }
-
- set owner(value) {
- if (this._owner !== value) {
- this._owner = value;
- this.dirty = true;
- }
- }
-
- /**
- * This property is for debugging only; it is not for production use nor is it optimized.
- * <p>
- * Draws the {@link DrawCommand#boundingVolume} for this command, assuming it is a sphere, when the command executes.
- * </p>
- *
- * @type {boolean}
- * @default false
- *
- * @see DrawCommand#boundingVolume
- */
- get debugShowBoundingVolume() {
- return hasFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME);
- }
-
- set debugShowBoundingVolume(value) {
- if (hasFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME) !== value) {
- setFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME, value);
- this.dirty = true;
- }
- }
-
- /**
- * Used to implement Scene.debugShowFrustums.
- * @ignore
- */
- get debugOverlappingFrustums() {
- return this._debugOverlappingFrustums;
- }
-
- set debugOverlappingFrustums(value) {
- if (this._debugOverlappingFrustums !== value) {
- this._debugOverlappingFrustums = value;
- this.dirty = true;
- }
- }
-
- /**
- * A GLSL string that will evaluate to a pick id. When <code>undefined</code>, the command will only draw depth
- * during the pick pass.
- *
- * @type {string|undefined}
- * @default undefined
- */
- get pickId() {
- return this._pickId;
- }
-
- set pickId(value) {
- if (this._pickId !== value) {
- this._pickId = value;
- this.dirty = true;
- }
- }
-
- /**
- * Whether metadata picking is allowed.
- *
- * This is essentially only set to `true` for draw commands that are
- * part of a `ModelDrawCommand`, to check whether a derived command
- * for metadata picking has to be created.
- *
- * @type {boolean}
- * @default undefined
- * @private
- */
- get pickMetadataAllowed() {
- return this._pickMetadataAllowed;
- }
-
- /**
- * Information about picked metadata.
- *
- * @type {PickedMetadataInfo|undefined}
- * @default undefined
- */
- get pickedMetadataInfo() {
- return this._pickedMetadataInfo;
- }
-
- set pickedMetadataInfo(value) {
- if (this._pickedMetadataInfo !== value) {
- this._pickedMetadataInfo = value;
- this.dirty = true;
- }
- }
-
- /**
- * Whether this command should be executed in the pick pass only.
- *
- * @type {boolean}
- * @default false
- */
- get pickOnly() {
- return hasFlag(this, Flags.PICK_ONLY);
- }
-
- set pickOnly(value) {
- if (hasFlag(this, Flags.PICK_ONLY) !== value) {
- setFlag(this, Flags.PICK_ONLY, value);
- this.dirty = true;
- }
- }
-
- /**
- * Whether this command should be derived to draw depth for classification of translucent primitives.
- *
- * @type {boolean}
- * @default false
- */
- get depthForTranslucentClassification() {
- return hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION);
- }
-
- set depthForTranslucentClassification(value) {
- if (hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION) !== value) {
- setFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION, value);
- this.dirty = true;
- }
- }
-
- /**
- * @param {DrawCommand} command
- * @param {DrawCommand} result
- * @returns {DrawCommand}
- * @private
- */
- static shallowClone(command, result) {
- if (!defined(command)) {
- return undefined;
- }
- if (!defined(result)) {
- result = new DrawCommand();
- }
-
- result._boundingVolume = command._boundingVolume;
- result._orientedBoundingBox = command._orientedBoundingBox;
- result._modelMatrix = command._modelMatrix;
- result._primitiveType = command._primitiveType;
- result._vertexArray = command._vertexArray;
- result._count = command._count;
- result._offset = command._offset;
- result._instanceCount = command._instanceCount;
- result._shaderProgram = command._shaderProgram;
- result._uniformMap = command._uniformMap;
- result._renderState = command._renderState;
- result._framebuffer = command._framebuffer;
- result._pass = command._pass;
- result._owner = command._owner;
- result._debugOverlappingFrustums = command._debugOverlappingFrustums;
- result._pickId = command._pickId;
- result._pickMetadataAllowed = command._pickMetadataAllowed;
- result._pickedMetadataInfo = command._pickedMetadataInfo;
- result._flags = command._flags;
-
- result.dirty = true;
- result.lastDirtyTime = 0;
-
- return result;
- }
-
- /**
- * Executes the draw command.
- *
- * @param {Context} context The renderer context in which to draw.
- * @param {PassState} [passState] The state for the current render pass.
- */
- execute(context, passState) {
- context.draw(this, passState);
- }
- }
-
- /**
- * @param {DrawCommand} command
- * @param {Flags} flag
- * @returns {boolean}
- * @ignore
- */
- function hasFlag(command, flag) {
- return (command._flags & flag) === flag;
- }
-
- /**
- * @param {DrawCommand} command
- * @param {Flags} flag
- * @param {boolean} value
- * @ignore
- */
- function setFlag(command, flag, value) {
- if (value) {
- command._flags |= flag;
- } else {
- command._flags &= ~flag;
- }
- }
-
- export default DrawCommand;
|