import BoundingRectangle from '../Core/BoundingRectangle.js';
|
import Color from '../Core/Color.js';
|
import defaultValue from '../Core/defaultValue.js';
|
import defined from '../Core/defined.js';
|
import DeveloperError from '../Core/DeveloperError.js';
|
import WebGLConstants from '../Core/WebGLConstants.js';
|
import WindingOrder from '../Core/WindingOrder.js';
|
import ContextLimits from './ContextLimits.js';
|
import freezeRenderState from './freezeRenderState.js';
|
|
function validateBlendEquation(blendEquation) {
|
return ((blendEquation === WebGLConstants.FUNC_ADD) ||
|
(blendEquation === WebGLConstants.FUNC_SUBTRACT) ||
|
(blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT) ||
|
(blendEquation === WebGLConstants.MIN) ||
|
(blendEquation === WebGLConstants.MAX));
|
}
|
|
function validateBlendFunction(blendFunction) {
|
return ((blendFunction === WebGLConstants.ZERO) ||
|
(blendFunction === WebGLConstants.ONE) ||
|
(blendFunction === WebGLConstants.SRC_COLOR) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_SRC_COLOR) ||
|
(blendFunction === WebGLConstants.DST_COLOR) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_DST_COLOR) ||
|
(blendFunction === WebGLConstants.SRC_ALPHA) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_SRC_ALPHA) ||
|
(blendFunction === WebGLConstants.DST_ALPHA) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_DST_ALPHA) ||
|
(blendFunction === WebGLConstants.CONSTANT_COLOR) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_COLOR) ||
|
(blendFunction === WebGLConstants.CONSTANT_ALPHA) ||
|
(blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_ALPHA) ||
|
(blendFunction === WebGLConstants.SRC_ALPHA_SATURATE));
|
}
|
|
function validateCullFace(cullFace) {
|
return ((cullFace === WebGLConstants.FRONT) ||
|
(cullFace === WebGLConstants.BACK) ||
|
(cullFace === WebGLConstants.FRONT_AND_BACK));
|
}
|
|
function validateDepthFunction(depthFunction) {
|
return ((depthFunction === WebGLConstants.NEVER) ||
|
(depthFunction === WebGLConstants.LESS) ||
|
(depthFunction === WebGLConstants.EQUAL) ||
|
(depthFunction === WebGLConstants.LEQUAL) ||
|
(depthFunction === WebGLConstants.GREATER) ||
|
(depthFunction === WebGLConstants.NOTEQUAL) ||
|
(depthFunction === WebGLConstants.GEQUAL) ||
|
(depthFunction === WebGLConstants.ALWAYS));
|
}
|
|
function validateStencilFunction(stencilFunction) {
|
return ((stencilFunction === WebGLConstants.NEVER) ||
|
(stencilFunction === WebGLConstants.LESS) ||
|
(stencilFunction === WebGLConstants.EQUAL) ||
|
(stencilFunction === WebGLConstants.LEQUAL) ||
|
(stencilFunction === WebGLConstants.GREATER) ||
|
(stencilFunction === WebGLConstants.NOTEQUAL) ||
|
(stencilFunction === WebGLConstants.GEQUAL) ||
|
(stencilFunction === WebGLConstants.ALWAYS));
|
}
|
|
function validateStencilOperation(stencilOperation) {
|
return ((stencilOperation === WebGLConstants.ZERO) ||
|
(stencilOperation === WebGLConstants.KEEP) ||
|
(stencilOperation === WebGLConstants.REPLACE) ||
|
(stencilOperation === WebGLConstants.INCR) ||
|
(stencilOperation === WebGLConstants.DECR) ||
|
(stencilOperation === WebGLConstants.INVERT) ||
|
(stencilOperation === WebGLConstants.INCR_WRAP) ||
|
(stencilOperation === WebGLConstants.DECR_WRAP));
|
}
|
|
/**
|
* @private
|
*/
|
function RenderState(renderState) {
|
var rs = defaultValue(renderState, defaultValue.EMPTY_OBJECT);
|
var cull = defaultValue(rs.cull, defaultValue.EMPTY_OBJECT);
|
var polygonOffset = defaultValue(rs.polygonOffset, defaultValue.EMPTY_OBJECT);
|
var scissorTest = defaultValue(rs.scissorTest, defaultValue.EMPTY_OBJECT);
|
var scissorTestRectangle = defaultValue(scissorTest.rectangle, defaultValue.EMPTY_OBJECT);
|
var depthRange = defaultValue(rs.depthRange, defaultValue.EMPTY_OBJECT);
|
var depthTest = defaultValue(rs.depthTest, defaultValue.EMPTY_OBJECT);
|
var colorMask = defaultValue(rs.colorMask, defaultValue.EMPTY_OBJECT);
|
var blending = defaultValue(rs.blending, defaultValue.EMPTY_OBJECT);
|
var blendingColor = defaultValue(blending.color, defaultValue.EMPTY_OBJECT);
|
var stencilTest = defaultValue(rs.stencilTest, defaultValue.EMPTY_OBJECT);
|
var stencilTestFrontOperation = defaultValue(stencilTest.frontOperation, defaultValue.EMPTY_OBJECT);
|
var stencilTestBackOperation = defaultValue(stencilTest.backOperation, defaultValue.EMPTY_OBJECT);
|
var sampleCoverage = defaultValue(rs.sampleCoverage, defaultValue.EMPTY_OBJECT);
|
var viewport = rs.viewport;
|
|
this.frontFace = defaultValue(rs.frontFace, WindingOrder.COUNTER_CLOCKWISE);
|
this.cull = {
|
enabled : defaultValue(cull.enabled, false),
|
face : defaultValue(cull.face, WebGLConstants.BACK)
|
};
|
this.lineWidth = defaultValue(rs.lineWidth, 1.0);
|
this.polygonOffset = {
|
enabled : defaultValue(polygonOffset.enabled, false),
|
factor : defaultValue(polygonOffset.factor, 0),
|
units : defaultValue(polygonOffset.units, 0)
|
};
|
this.scissorTest = {
|
enabled : defaultValue(scissorTest.enabled, false),
|
rectangle : BoundingRectangle.clone(scissorTestRectangle)
|
};
|
this.depthRange = {
|
near : defaultValue(depthRange.near, 0),
|
far : defaultValue(depthRange.far, 1)
|
};
|
this.depthTest = {
|
enabled : defaultValue(depthTest.enabled, false),
|
func : defaultValue(depthTest.func, WebGLConstants.LESS) // func, because function is a JavaScript keyword
|
};
|
this.colorMask = {
|
red : defaultValue(colorMask.red, true),
|
green : defaultValue(colorMask.green, true),
|
blue : defaultValue(colorMask.blue, true),
|
alpha : defaultValue(colorMask.alpha, true)
|
};
|
this.depthMask = defaultValue(rs.depthMask, true);
|
this.stencilMask = defaultValue(rs.stencilMask, ~0);
|
this.blending = {
|
enabled : defaultValue(blending.enabled, false),
|
color : new Color(
|
defaultValue(blendingColor.red, 0.0),
|
defaultValue(blendingColor.green, 0.0),
|
defaultValue(blendingColor.blue, 0.0),
|
defaultValue(blendingColor.alpha, 0.0)
|
),
|
equationRgb : defaultValue(blending.equationRgb, WebGLConstants.FUNC_ADD),
|
equationAlpha : defaultValue(blending.equationAlpha, WebGLConstants.FUNC_ADD),
|
functionSourceRgb : defaultValue(blending.functionSourceRgb, WebGLConstants.ONE),
|
functionSourceAlpha : defaultValue(blending.functionSourceAlpha, WebGLConstants.ONE),
|
functionDestinationRgb : defaultValue(blending.functionDestinationRgb, WebGLConstants.ZERO),
|
functionDestinationAlpha : defaultValue(blending.functionDestinationAlpha, WebGLConstants.ZERO)
|
};
|
this.stencilTest = {
|
enabled : defaultValue(stencilTest.enabled, false),
|
frontFunction : defaultValue(stencilTest.frontFunction, WebGLConstants.ALWAYS),
|
backFunction : defaultValue(stencilTest.backFunction, WebGLConstants.ALWAYS),
|
reference : defaultValue(stencilTest.reference, 0),
|
mask : defaultValue(stencilTest.mask, ~0),
|
frontOperation : {
|
fail : defaultValue(stencilTestFrontOperation.fail, WebGLConstants.KEEP),
|
zFail : defaultValue(stencilTestFrontOperation.zFail, WebGLConstants.KEEP),
|
zPass : defaultValue(stencilTestFrontOperation.zPass, WebGLConstants.KEEP)
|
},
|
backOperation : {
|
fail : defaultValue(stencilTestBackOperation.fail, WebGLConstants.KEEP),
|
zFail : defaultValue(stencilTestBackOperation.zFail, WebGLConstants.KEEP),
|
zPass : defaultValue(stencilTestBackOperation.zPass, WebGLConstants.KEEP)
|
}
|
};
|
this.sampleCoverage = {
|
enabled : defaultValue(sampleCoverage.enabled, false),
|
value : defaultValue(sampleCoverage.value, 1.0),
|
invert : defaultValue(sampleCoverage.invert, false)
|
};
|
this.viewport = (defined(viewport)) ? new BoundingRectangle(viewport.x, viewport.y, viewport.width, viewport.height) : undefined;
|
|
//>>includeStart('debug', pragmas.debug);
|
if ((this.lineWidth < ContextLimits.minimumAliasedLineWidth) ||
|
(this.lineWidth > ContextLimits.maximumAliasedLineWidth)) {
|
throw new DeveloperError('renderState.lineWidth is out of range. Check minimumAliasedLineWidth and maximumAliasedLineWidth.');
|
}
|
if (!WindingOrder.validate(this.frontFace)) {
|
throw new DeveloperError('Invalid renderState.frontFace.');
|
}
|
if (!validateCullFace(this.cull.face)) {
|
throw new DeveloperError('Invalid renderState.cull.face.');
|
}
|
if ((this.scissorTest.rectangle.width < 0) ||
|
(this.scissorTest.rectangle.height < 0)) {
|
throw new DeveloperError('renderState.scissorTest.rectangle.width and renderState.scissorTest.rectangle.height must be greater than or equal to zero.');
|
}
|
if (this.depthRange.near > this.depthRange.far) {
|
// WebGL specific - not an error in GL ES
|
throw new DeveloperError('renderState.depthRange.near can not be greater than renderState.depthRange.far.');
|
}
|
if (this.depthRange.near < 0) {
|
// Would be clamped by GL
|
throw new DeveloperError('renderState.depthRange.near must be greater than or equal to zero.');
|
}
|
if (this.depthRange.far > 1) {
|
// Would be clamped by GL
|
throw new DeveloperError('renderState.depthRange.far must be less than or equal to one.');
|
}
|
if (!validateDepthFunction(this.depthTest.func)) {
|
throw new DeveloperError('Invalid renderState.depthTest.func.');
|
}
|
if ((this.blending.color.red < 0.0) || (this.blending.color.red > 1.0) ||
|
(this.blending.color.green < 0.0) || (this.blending.color.green > 1.0) ||
|
(this.blending.color.blue < 0.0) || (this.blending.color.blue > 1.0) ||
|
(this.blending.color.alpha < 0.0) || (this.blending.color.alpha > 1.0)) {
|
// Would be clamped by GL
|
throw new DeveloperError('renderState.blending.color components must be greater than or equal to zero and less than or equal to one.');
|
}
|
if (!validateBlendEquation(this.blending.equationRgb)) {
|
throw new DeveloperError('Invalid renderState.blending.equationRgb.');
|
}
|
if (!validateBlendEquation(this.blending.equationAlpha)) {
|
throw new DeveloperError('Invalid renderState.blending.equationAlpha.');
|
}
|
if (!validateBlendFunction(this.blending.functionSourceRgb)) {
|
throw new DeveloperError('Invalid renderState.blending.functionSourceRgb.');
|
}
|
if (!validateBlendFunction(this.blending.functionSourceAlpha)) {
|
throw new DeveloperError('Invalid renderState.blending.functionSourceAlpha.');
|
}
|
if (!validateBlendFunction(this.blending.functionDestinationRgb)) {
|
throw new DeveloperError('Invalid renderState.blending.functionDestinationRgb.');
|
}
|
if (!validateBlendFunction(this.blending.functionDestinationAlpha)) {
|
throw new DeveloperError('Invalid renderState.blending.functionDestinationAlpha.');
|
}
|
if (!validateStencilFunction(this.stencilTest.frontFunction)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.frontFunction.');
|
}
|
if (!validateStencilFunction(this.stencilTest.backFunction)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.backFunction.');
|
}
|
if (!validateStencilOperation(this.stencilTest.frontOperation.fail)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.fail.');
|
}
|
if (!validateStencilOperation(this.stencilTest.frontOperation.zFail)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zFail.');
|
}
|
if (!validateStencilOperation(this.stencilTest.frontOperation.zPass)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zPass.');
|
}
|
if (!validateStencilOperation(this.stencilTest.backOperation.fail)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.backOperation.fail.');
|
}
|
if (!validateStencilOperation(this.stencilTest.backOperation.zFail)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zFail.');
|
}
|
if (!validateStencilOperation(this.stencilTest.backOperation.zPass)) {
|
throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zPass.');
|
}
|
|
if (defined(this.viewport)) {
|
if (this.viewport.width < 0) {
|
throw new DeveloperError('renderState.viewport.width must be greater than or equal to zero.');
|
}
|
if (this.viewport.height < 0) {
|
throw new DeveloperError('renderState.viewport.height must be greater than or equal to zero.');
|
}
|
|
if (this.viewport.width > ContextLimits.maximumViewportWidth) {
|
throw new DeveloperError('renderState.viewport.width must be less than or equal to the maximum viewport width (' + ContextLimits.maximumViewportWidth.toString() + '). Check maximumViewportWidth.');
|
}
|
if (this.viewport.height > ContextLimits.maximumViewportHeight) {
|
throw new DeveloperError('renderState.viewport.height must be less than or equal to the maximum viewport height (' + ContextLimits.maximumViewportHeight.toString() + '). Check maximumViewportHeight.');
|
}
|
}
|
//>>includeEnd('debug');
|
|
this.id = 0;
|
this._applyFunctions = [];
|
}
|
|
var nextRenderStateId = 0;
|
var renderStateCache = {};
|
|
/**
|
* Validates and then finds or creates an immutable render state, which defines the pipeline
|
* state for a {@link DrawCommand} or {@link ClearCommand}. All inputs states are optional. Omitted states
|
* use the defaults shown in the example below.
|
*
|
* @param {Object} [renderState] The states defining the render state as shown in the example below.
|
*
|
* @exception {RuntimeError} renderState.lineWidth is out of range.
|
* @exception {DeveloperError} Invalid renderState.frontFace.
|
* @exception {DeveloperError} Invalid renderState.cull.face.
|
* @exception {DeveloperError} scissorTest.rectangle.width and scissorTest.rectangle.height must be greater than or equal to zero.
|
* @exception {DeveloperError} renderState.depthRange.near can't be greater than renderState.depthRange.far.
|
* @exception {DeveloperError} renderState.depthRange.near must be greater than or equal to zero.
|
* @exception {DeveloperError} renderState.depthRange.far must be less than or equal to zero.
|
* @exception {DeveloperError} Invalid renderState.depthTest.func.
|
* @exception {DeveloperError} renderState.blending.color components must be greater than or equal to zero and less than or equal to one
|
* @exception {DeveloperError} Invalid renderState.blending.equationRgb.
|
* @exception {DeveloperError} Invalid renderState.blending.equationAlpha.
|
* @exception {DeveloperError} Invalid renderState.blending.functionSourceRgb.
|
* @exception {DeveloperError} Invalid renderState.blending.functionSourceAlpha.
|
* @exception {DeveloperError} Invalid renderState.blending.functionDestinationRgb.
|
* @exception {DeveloperError} Invalid renderState.blending.functionDestinationAlpha.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.frontFunction.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.backFunction.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.fail.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zFail.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zPass.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.fail.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zFail.
|
* @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zPass.
|
* @exception {DeveloperError} renderState.viewport.width must be greater than or equal to zero.
|
* @exception {DeveloperError} renderState.viewport.width must be less than or equal to the maximum viewport width.
|
* @exception {DeveloperError} renderState.viewport.height must be greater than or equal to zero.
|
* @exception {DeveloperError} renderState.viewport.height must be less than or equal to the maximum viewport height.
|
*
|
*
|
* @example
|
* var defaults = {
|
* frontFace : WindingOrder.COUNTER_CLOCKWISE,
|
* cull : {
|
* enabled : false,
|
* face : CullFace.BACK
|
* },
|
* lineWidth : 1,
|
* polygonOffset : {
|
* enabled : false,
|
* factor : 0,
|
* units : 0
|
* },
|
* scissorTest : {
|
* enabled : false,
|
* rectangle : {
|
* x : 0,
|
* y : 0,
|
* width : 0,
|
* height : 0
|
* }
|
* },
|
* depthRange : {
|
* near : 0,
|
* far : 1
|
* },
|
* depthTest : {
|
* enabled : false,
|
* func : DepthFunction.LESS
|
* },
|
* colorMask : {
|
* red : true,
|
* green : true,
|
* blue : true,
|
* alpha : true
|
* },
|
* depthMask : true,
|
* stencilMask : ~0,
|
* blending : {
|
* enabled : false,
|
* color : {
|
* red : 0.0,
|
* green : 0.0,
|
* blue : 0.0,
|
* alpha : 0.0
|
* },
|
* equationRgb : BlendEquation.ADD,
|
* equationAlpha : BlendEquation.ADD,
|
* functionSourceRgb : BlendFunction.ONE,
|
* functionSourceAlpha : BlendFunction.ONE,
|
* functionDestinationRgb : BlendFunction.ZERO,
|
* functionDestinationAlpha : BlendFunction.ZERO
|
* },
|
* stencilTest : {
|
* enabled : false,
|
* frontFunction : StencilFunction.ALWAYS,
|
* backFunction : StencilFunction.ALWAYS,
|
* reference : 0,
|
* mask : ~0,
|
* frontOperation : {
|
* fail : StencilOperation.KEEP,
|
* zFail : StencilOperation.KEEP,
|
* zPass : StencilOperation.KEEP
|
* },
|
* backOperation : {
|
* fail : StencilOperation.KEEP,
|
* zFail : StencilOperation.KEEP,
|
* zPass : StencilOperation.KEEP
|
* }
|
* },
|
* sampleCoverage : {
|
* enabled : false,
|
* value : 1.0,
|
* invert : false
|
* }
|
* };
|
*
|
* var rs = RenderState.fromCache(defaults);
|
*
|
* @see DrawCommand
|
* @see ClearCommand
|
*
|
* @private
|
*/
|
RenderState.fromCache = function(renderState) {
|
var partialKey = JSON.stringify(renderState);
|
var cachedState = renderStateCache[partialKey];
|
if (defined(cachedState)) {
|
++cachedState.referenceCount;
|
return cachedState.state;
|
}
|
|
// Cache miss. Fully define render state and try again.
|
var states = new RenderState(renderState);
|
var fullKey = JSON.stringify(states);
|
cachedState = renderStateCache[fullKey];
|
if (!defined(cachedState)) {
|
states.id = nextRenderStateId++;
|
//>>includeStart('debug', pragmas.debug);
|
states = freezeRenderState(states);
|
//>>includeEnd('debug');
|
cachedState = {
|
referenceCount : 0,
|
state : states
|
};
|
|
// Cache full render state. Multiple partially defined render states may map to this.
|
renderStateCache[fullKey] = cachedState;
|
}
|
|
++cachedState.referenceCount;
|
|
// Cache partial render state so we can skip validation on a cache hit for a partially defined render state
|
renderStateCache[partialKey] = {
|
referenceCount : 1,
|
state : cachedState.state
|
};
|
|
return cachedState.state;
|
};
|
|
/**
|
* @private
|
*/
|
RenderState.removeFromCache = function(renderState) {
|
var states = new RenderState(renderState);
|
var fullKey = JSON.stringify(states);
|
var fullCachedState = renderStateCache[fullKey];
|
|
// decrement partial key reference count
|
var partialKey = JSON.stringify(renderState);
|
var cachedState = renderStateCache[partialKey];
|
if (defined(cachedState)) {
|
--cachedState.referenceCount;
|
|
if (cachedState.referenceCount === 0) {
|
// remove partial key
|
delete renderStateCache[partialKey];
|
|
// decrement full key reference count
|
if (defined(fullCachedState)) {
|
--fullCachedState.referenceCount;
|
}
|
}
|
}
|
|
// remove full key if reference count is zero
|
if (defined(fullCachedState) && (fullCachedState.referenceCount === 0)) {
|
delete renderStateCache[fullKey];
|
}
|
};
|
|
/**
|
* This function is for testing purposes only.
|
* @private
|
*/
|
RenderState.getCache = function() {
|
return renderStateCache;
|
};
|
|
/**
|
* This function is for testing purposes only.
|
* @private
|
*/
|
RenderState.clearCache = function() {
|
renderStateCache = {};
|
};
|
|
function enableOrDisable(gl, glEnum, enable) {
|
if (enable) {
|
gl.enable(glEnum);
|
} else {
|
gl.disable(glEnum);
|
}
|
}
|
|
function applyFrontFace(gl, renderState) {
|
gl.frontFace(renderState.frontFace);
|
}
|
|
function applyCull(gl, renderState) {
|
var cull = renderState.cull;
|
var enabled = cull.enabled;
|
|
enableOrDisable(gl, gl.CULL_FACE, enabled);
|
|
if (enabled) {
|
gl.cullFace(cull.face);
|
}
|
}
|
|
function applyLineWidth(gl, renderState) {
|
gl.lineWidth(renderState.lineWidth);
|
}
|
|
function applyPolygonOffset(gl, renderState) {
|
var polygonOffset = renderState.polygonOffset;
|
var enabled = polygonOffset.enabled;
|
|
enableOrDisable(gl, gl.POLYGON_OFFSET_FILL, enabled);
|
|
if (enabled) {
|
gl.polygonOffset(polygonOffset.factor, polygonOffset.units);
|
}
|
}
|
|
function applyScissorTest(gl, renderState, passState) {
|
var scissorTest = renderState.scissorTest;
|
var enabled = (defined(passState.scissorTest)) ? passState.scissorTest.enabled : scissorTest.enabled;
|
|
enableOrDisable(gl, gl.SCISSOR_TEST, enabled);
|
|
if (enabled) {
|
var rectangle = (defined(passState.scissorTest)) ? passState.scissorTest.rectangle : scissorTest.rectangle;
|
gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
}
|
}
|
|
function applyDepthRange(gl, renderState) {
|
var depthRange = renderState.depthRange;
|
gl.depthRange(depthRange.near, depthRange.far);
|
}
|
|
function applyDepthTest(gl, renderState) {
|
var depthTest = renderState.depthTest;
|
var enabled = depthTest.enabled;
|
|
enableOrDisable(gl, gl.DEPTH_TEST, enabled);
|
|
if (enabled) {
|
gl.depthFunc(depthTest.func);
|
}
|
}
|
|
function applyColorMask(gl, renderState) {
|
var colorMask = renderState.colorMask;
|
gl.colorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
|
}
|
|
function applyDepthMask(gl, renderState) {
|
gl.depthMask(renderState.depthMask);
|
}
|
|
function applyStencilMask(gl, renderState) {
|
gl.stencilMask(renderState.stencilMask);
|
}
|
|
function applyBlendingColor(gl, color) {
|
gl.blendColor(color.red, color.green, color.blue, color.alpha);
|
}
|
|
function applyBlending(gl, renderState, passState) {
|
var blending = renderState.blending;
|
var enabled = (defined(passState.blendingEnabled)) ? passState.blendingEnabled : blending.enabled;
|
|
enableOrDisable(gl, gl.BLEND, enabled);
|
|
if (enabled) {
|
applyBlendingColor(gl, blending.color);
|
gl.blendEquationSeparate(blending.equationRgb, blending.equationAlpha);
|
gl.blendFuncSeparate(blending.functionSourceRgb, blending.functionDestinationRgb, blending.functionSourceAlpha, blending.functionDestinationAlpha);
|
}
|
}
|
|
function applyStencilTest(gl, renderState) {
|
var stencilTest = renderState.stencilTest;
|
var enabled = stencilTest.enabled;
|
|
enableOrDisable(gl, gl.STENCIL_TEST, enabled);
|
|
if (enabled) {
|
var frontFunction = stencilTest.frontFunction;
|
var backFunction = stencilTest.backFunction;
|
var reference = stencilTest.reference;
|
var mask = stencilTest.mask;
|
|
// Section 6.8 of the WebGL spec requires the reference and masks to be the same for
|
// front- and back-face tests. This call prevents invalid operation errors when calling
|
// stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this.
|
gl.stencilFunc(frontFunction, reference, mask);
|
gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask);
|
gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask);
|
|
var frontOperation = stencilTest.frontOperation;
|
var frontOperationFail = frontOperation.fail;
|
var frontOperationZFail = frontOperation.zFail;
|
var frontOperationZPass = frontOperation.zPass;
|
|
gl.stencilOpSeparate(gl.FRONT, frontOperationFail, frontOperationZFail, frontOperationZPass);
|
|
var backOperation = stencilTest.backOperation;
|
var backOperationFail = backOperation.fail;
|
var backOperationZFail = backOperation.zFail;
|
var backOperationZPass = backOperation.zPass;
|
|
gl.stencilOpSeparate(gl.BACK, backOperationFail, backOperationZFail, backOperationZPass);
|
}
|
}
|
|
function applySampleCoverage(gl, renderState) {
|
var sampleCoverage = renderState.sampleCoverage;
|
var enabled = sampleCoverage.enabled;
|
|
enableOrDisable(gl, gl.SAMPLE_COVERAGE, enabled);
|
|
if (enabled) {
|
gl.sampleCoverage(sampleCoverage.value, sampleCoverage.invert);
|
}
|
}
|
|
var scratchViewport = new BoundingRectangle();
|
|
function applyViewport(gl, renderState, passState) {
|
var viewport = defaultValue(renderState.viewport, passState.viewport);
|
if (!defined(viewport)) {
|
viewport = scratchViewport;
|
viewport.width = passState.context.drawingBufferWidth;
|
viewport.height = passState.context.drawingBufferHeight;
|
}
|
|
passState.context.uniformState.viewport = viewport;
|
gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
}
|
|
RenderState.apply = function(gl, renderState, passState) {
|
applyFrontFace(gl, renderState);
|
applyCull(gl, renderState);
|
applyLineWidth(gl, renderState);
|
applyPolygonOffset(gl, renderState);
|
applyDepthRange(gl, renderState);
|
applyDepthTest(gl, renderState);
|
applyColorMask(gl, renderState);
|
applyDepthMask(gl, renderState);
|
applyStencilMask(gl, renderState);
|
applyStencilTest(gl, renderState);
|
applySampleCoverage(gl, renderState);
|
applyScissorTest(gl, renderState, passState);
|
applyBlending(gl, renderState, passState);
|
applyViewport(gl, renderState, passState);
|
};
|
|
function createFuncs(previousState, nextState) {
|
var funcs = [];
|
|
if (previousState.frontFace !== nextState.frontFace) {
|
funcs.push(applyFrontFace);
|
}
|
|
if ((previousState.cull.enabled !== nextState.cull.enabled) || (previousState.cull.face !== nextState.cull.face)) {
|
funcs.push(applyCull);
|
}
|
|
if (previousState.lineWidth !== nextState.lineWidth) {
|
funcs.push(applyLineWidth);
|
}
|
|
if ((previousState.polygonOffset.enabled !== nextState.polygonOffset.enabled) ||
|
(previousState.polygonOffset.factor !== nextState.polygonOffset.factor) ||
|
(previousState.polygonOffset.units !== nextState.polygonOffset.units)) {
|
funcs.push(applyPolygonOffset);
|
}
|
|
if ((previousState.depthRange.near !== nextState.depthRange.near) || (previousState.depthRange.far !== nextState.depthRange.far)) {
|
funcs.push(applyDepthRange);
|
}
|
|
if ((previousState.depthTest.enabled !== nextState.depthTest.enabled) || (previousState.depthTest.func !== nextState.depthTest.func)) {
|
funcs.push(applyDepthTest);
|
}
|
|
if ((previousState.colorMask.red !== nextState.colorMask.red) ||
|
(previousState.colorMask.green !== nextState.colorMask.green) ||
|
(previousState.colorMask.blue !== nextState.colorMask.blue) ||
|
(previousState.colorMask.alpha !== nextState.colorMask.alpha)) {
|
funcs.push(applyColorMask);
|
}
|
|
if (previousState.depthMask !== nextState.depthMask) {
|
funcs.push(applyDepthMask);
|
}
|
|
if (previousState.stencilMask !== nextState.stencilMask) {
|
funcs.push(applyStencilMask);
|
}
|
|
if ((previousState.stencilTest.enabled !== nextState.stencilTest.enabled) ||
|
(previousState.stencilTest.frontFunction !== nextState.stencilTest.frontFunction) ||
|
(previousState.stencilTest.backFunction !== nextState.stencilTest.backFunction) ||
|
(previousState.stencilTest.reference !== nextState.stencilTest.reference) ||
|
(previousState.stencilTest.mask !== nextState.stencilTest.mask) ||
|
(previousState.stencilTest.frontOperation.fail !== nextState.stencilTest.frontOperation.fail) ||
|
(previousState.stencilTest.frontOperation.zFail !== nextState.stencilTest.frontOperation.zFail) ||
|
(previousState.stencilTest.backOperation.fail !== nextState.stencilTest.backOperation.fail) ||
|
(previousState.stencilTest.backOperation.zFail !== nextState.stencilTest.backOperation.zFail) ||
|
(previousState.stencilTest.backOperation.zPass !== nextState.stencilTest.backOperation.zPass)) {
|
funcs.push(applyStencilTest);
|
}
|
|
if ((previousState.sampleCoverage.enabled !== nextState.sampleCoverage.enabled) ||
|
(previousState.sampleCoverage.value !== nextState.sampleCoverage.value) ||
|
(previousState.sampleCoverage.invert !== nextState.sampleCoverage.invert)) {
|
funcs.push(applySampleCoverage);
|
}
|
|
return funcs;
|
}
|
|
RenderState.partialApply = function(gl, previousRenderState, renderState, previousPassState, passState, clear) {
|
if (previousRenderState !== renderState) {
|
// When a new render state is applied, instead of making WebGL calls for all the states or first
|
// comparing the states one-by-one with the previous state (basically a linear search), we take
|
// advantage of RenderState's immutability, and store a dynamically populated sparse data structure
|
// containing functions that make the minimum number of WebGL calls when transitioning from one state
|
// to the other. In practice, this works well since state-to-state transitions generally only require a
|
// few WebGL calls, especially if commands are stored by state.
|
var funcs = renderState._applyFunctions[previousRenderState.id];
|
if (!defined(funcs)) {
|
funcs = createFuncs(previousRenderState, renderState);
|
renderState._applyFunctions[previousRenderState.id] = funcs;
|
}
|
|
var len = funcs.length;
|
for (var i = 0; i < len; ++i) {
|
funcs[i](gl, renderState);
|
}
|
}
|
|
var previousScissorTest = (defined(previousPassState.scissorTest)) ? previousPassState.scissorTest : previousRenderState.scissorTest;
|
var scissorTest = (defined(passState.scissorTest)) ? passState.scissorTest : renderState.scissorTest;
|
|
// Our scissor rectangle can get out of sync with the GL scissor rectangle on clears.
|
// Seems to be a problem only on ANGLE. See https://github.com/CesiumGS/cesium/issues/2994
|
if ((previousScissorTest !== scissorTest) || clear) {
|
applyScissorTest(gl, renderState, passState);
|
}
|
|
var previousBlendingEnabled = (defined(previousPassState.blendingEnabled)) ? previousPassState.blendingEnabled : previousRenderState.blending.enabled;
|
var blendingEnabled = (defined(passState.blendingEnabled)) ? passState.blendingEnabled : renderState.blending.enabled;
|
if ((previousBlendingEnabled !== blendingEnabled) ||
|
(blendingEnabled && (previousRenderState.blending !== renderState.blending))) {
|
applyBlending(gl, renderState, passState);
|
}
|
|
if (previousRenderState !== renderState || previousPassState !== passState || previousPassState.context !== passState.context) {
|
applyViewport(gl, renderState, passState);
|
}
|
};
|
|
RenderState.getState = function(renderState) {
|
//>>includeStart('debug', pragmas.debug);
|
if (!defined(renderState)) {
|
throw new DeveloperError('renderState is required.');
|
}
|
//>>includeEnd('debug');
|
|
return {
|
frontFace : renderState.frontFace,
|
cull : {
|
enabled : renderState.cull.enabled,
|
face : renderState.cull.face
|
},
|
lineWidth : renderState.lineWidth,
|
polygonOffset : {
|
enabled : renderState.polygonOffset.enabled,
|
factor : renderState.polygonOffset.factor,
|
units : renderState.polygonOffset.units
|
},
|
scissorTest : {
|
enabled : renderState.scissorTest.enabled,
|
rectangle : BoundingRectangle.clone(renderState.scissorTest.rectangle)
|
},
|
depthRange : {
|
near : renderState.depthRange.near,
|
far : renderState.depthRange.far
|
},
|
depthTest : {
|
enabled : renderState.depthTest.enabled,
|
func : renderState.depthTest.func
|
},
|
colorMask : {
|
red : renderState.colorMask.red,
|
green : renderState.colorMask.green,
|
blue : renderState.colorMask.blue,
|
alpha : renderState.colorMask.alpha
|
},
|
depthMask : renderState.depthMask,
|
stencilMask : renderState.stencilMask,
|
blending : {
|
enabled : renderState.blending.enabled,
|
color : Color.clone(renderState.blending.color),
|
equationRgb : renderState.blending.equationRgb,
|
equationAlpha : renderState.blending.equationAlpha,
|
functionSourceRgb : renderState.blending.functionSourceRgb,
|
functionSourceAlpha : renderState.blending.functionSourceAlpha,
|
functionDestinationRgb : renderState.blending.functionDestinationRgb,
|
functionDestinationAlpha : renderState.blending.functionDestinationAlpha
|
},
|
stencilTest : {
|
enabled : renderState.stencilTest.enabled,
|
frontFunction : renderState.stencilTest.frontFunction,
|
backFunction : renderState.stencilTest.backFunction,
|
reference : renderState.stencilTest.reference,
|
mask : renderState.stencilTest.mask,
|
frontOperation : {
|
fail : renderState.stencilTest.frontOperation.fail,
|
zFail : renderState.stencilTest.frontOperation.zFail,
|
zPass : renderState.stencilTest.frontOperation.zPass
|
},
|
backOperation : {
|
fail : renderState.stencilTest.backOperation.fail,
|
zFail : renderState.stencilTest.backOperation.zFail,
|
zPass : renderState.stencilTest.backOperation.zPass
|
}
|
},
|
sampleCoverage : {
|
enabled : renderState.sampleCoverage.enabled,
|
value : renderState.sampleCoverage.value,
|
invert : renderState.sampleCoverage.invert
|
},
|
viewport : defined(renderState.viewport) ? BoundingRectangle.clone(renderState.viewport) : undefined
|
};
|
};
|
export default RenderState;
|