import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import PrimitiveType from "../Core/PrimitiveType.js"; 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, }; /** * Represents a command to the renderer for drawing. * * @private */ function DrawCommand(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._boundingVolume = options.boundingVolume; this._orientedBoundingBox = options.orientedBoundingBox; this._modelMatrix = options.modelMatrix; this._primitiveType = defaultValue( options.primitiveType, PrimitiveType.TRIANGLES ); this._vertexArray = options.vertexArray; this._count = options.count; this._offset = defaultValue(options.offset, 0); this._instanceCount = defaultValue(options.instanceCount, 0); this._shaderProgram = options.shaderProgram; this._uniformMap = options.uniformMap; this._renderState = options.renderState; this._framebuffer = options.framebuffer; this._pass = options.pass; this._owner = options.owner; this._debugOverlappingFrustums = 0; this._pickId = options.pickId; // Set initial flags. this._flags = 0; this.cull = defaultValue(options.cull, true); this.occlude = defaultValue(options.occlude, true); this.executeInClosestFrustum = defaultValue( options.executeInClosestFrustum, false ); this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this.castShadows = defaultValue(options.castShadows, false); this.receiveShadows = defaultValue(options.receiveShadows, false); this.pickOnly = defaultValue(options.pickOnly, false); this.depthForTranslucentClassification = defaultValue( options.depthForTranslucentClassification, false ); this.dirty = true; this.lastDirtyTime = 0; /** * @private */ this.derivedCommands = {}; } function hasFlag(command, flag) { return (command._flags & flag) === flag; } function setFlag(command, flag, value) { if (value) { command._flags |= flag; } else { command._flags &= ~flag; } } Object.defineProperties(DrawCommand.prototype, { /** * The bounding volume of the geometry in world space. This is used for culling and frustum selection. *
* For best rendering performance, use the tightest possible bounding volume. Although
* undefined
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.
*
true
, the renderer frustum and horizon culls the command based on its {@link DrawCommand#boundingVolume}.
* If the command was already culled, set this to false
for a performance improvement.
*
* @memberof DrawCommand.prototype
* @type {Boolean}
* @default true
*/
cull: {
get: function () {
return hasFlag(this, Flags.CULL);
},
set: function (value) {
if (hasFlag(this, Flags.CULL) !== value) {
setFlag(this, Flags.CULL, value);
this.dirty = true;
}
},
},
/**
* When true
, the horizon culls the command based on its {@link DrawCommand#boundingVolume}.
* {@link DrawCommand#cull} must also be true
in order for the command to be culled.
*
* @memberof DrawCommand.prototype
* @type {Boolean}
* @default true
*/
occlude: {
get: function () {
return hasFlag(this, Flags.OCCLUDE);
},
set: function (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.
*
* When undefined
, the geometry is assumed to be defined in world space.
*
false
.
*
* @memberof DrawCommand.prototype
* @type {Boolean}
* @default false
*/
executeInClosestFrustum: {
get: function () {
return hasFlag(this, Flags.EXECUTE_IN_CLOSEST_FRUSTUM);
},
set: function (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}.
*
* @memberof DrawCommand.prototype
* @type {Object}
* @default undefined
*
* @see Scene#debugCommandFilter
*/
owner: {
get: function () {
return this._owner;
},
set: function (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.
* * Draws the {@link DrawCommand#boundingVolume} for this command, assuming it is a sphere, when the command executes. *
* * @memberof DrawCommand.prototype * @type {Boolean} * @default false * * @see DrawCommand#boundingVolume */ debugShowBoundingVolume: { get: function () { return hasFlag(this, Flags.DEBUG_SHOW_BOUNDING_VOLUME); }, set: function (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. * @private */ debugOverlappingFrustums: { get: function () { return this._debugOverlappingFrustums; }, set: function (value) { if (this._debugOverlappingFrustums !== value) { this._debugOverlappingFrustums = value; this.dirty = true; } }, }, /** * A GLSL string that will evaluate to a pick id. Whenundefined
, the command will only draw depth
* during the pick pass.
*
* @memberof DrawCommand.prototype
* @type {String}
* @default undefined
*/
pickId: {
get: function () {
return this._pickId;
},
set: function (value) {
if (this._pickId !== value) {
this._pickId = value;
this.dirty = true;
}
},
},
/**
* Whether this command should be executed in the pick pass only.
*
* @memberof DrawCommand.prototype
* @type {Boolean}
* @default false
*/
pickOnly: {
get: function () {
return hasFlag(this, Flags.PICK_ONLY);
},
set: function (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.
*
* @memberof DrawCommand.prototype
* @type {Boolean}
* @default false
*/
depthForTranslucentClassification: {
get: function () {
return hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION);
},
set: function (value) {
if (hasFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION) !== value) {
setFlag(this, Flags.DEPTH_FOR_TRANSLUCENT_CLASSIFICATION, value);
this.dirty = true;
}
},
},
});
/**
* @private
*/
DrawCommand.shallowClone = function (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._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.
*/
DrawCommand.prototype.execute = function (context, passState) {
context.draw(this, passState);
};
export default DrawCommand;