MCP 3D Printer Server
by DMontgomery40
Verified
import {
CullFaceNone, CullFaceBack, CullFaceFront, DoubleSide, BackSide,
NormalBlending, NoBlending, CustomBlending, AddEquation,
AdditiveBlending, SubtractiveBlending, MultiplyBlending, SubtractEquation, ReverseSubtractEquation,
ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor,
OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor,
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth
} from '../../../constants.js';
import { Vector4 } from '../../../math/Vector4.js';
let equationToGL, factorToGL;
/**
* A WebGL 2 backend utility module for managing the WebGL state.
*
* The major goal of this module is to reduce the number of state changes
* by caching the WEbGL state with a series of variables. In this way, the
* renderer only executes state change commands when necessary which
* improves the overall performance.
*
* @private
*/
class WebGLState {
/**
* Constructs a new utility object.
*
* @param {WebGLBackend} backend - The WebGL 2 backend.
*/
constructor( backend ) {
/**
* A reference to the WebGL 2 backend.
*
* @type {WebGLBackend}
*/
this.backend = backend;
/**
* A reference to the rendering context.
*
* @type {WebGL2RenderingContext}
*/
this.gl = this.backend.gl;
// Below properties are intended to cache
// the WebGL state and are not explicitly
// documented for convenience reasons.
this.enabled = {};
this.currentFlipSided = null;
this.currentCullFace = null;
this.currentProgram = null;
this.currentBlendingEnabled = false;
this.currentBlending = null;
this.currentBlendSrc = null;
this.currentBlendDst = null;
this.currentBlendSrcAlpha = null;
this.currentBlendDstAlpha = null;
this.currentPremultipledAlpha = null;
this.currentPolygonOffsetFactor = null;
this.currentPolygonOffsetUnits = null;
this.currentColorMask = null;
this.currentDepthFunc = null;
this.currentDepthMask = null;
this.currentStencilFunc = null;
this.currentStencilRef = null;
this.currentStencilFuncMask = null;
this.currentStencilFail = null;
this.currentStencilZFail = null;
this.currentStencilZPass = null;
this.currentStencilMask = null;
this.currentLineWidth = null;
this.currentClippingPlanes = 0;
this.currentBoundFramebuffers = {};
this.currentDrawbuffers = new WeakMap();
this.maxTextures = this.gl.getParameter( this.gl.MAX_TEXTURE_IMAGE_UNITS );
this.currentTextureSlot = null;
this.currentBoundTextures = {};
this.currentBoundBufferBases = {};
this._init();
}
/**
* Inits the state of the utility.
*
* @private
*/
_init() {
const gl = this.gl;
// Store only WebGL constants here.
equationToGL = {
[ AddEquation ]: gl.FUNC_ADD,
[ SubtractEquation ]: gl.FUNC_SUBTRACT,
[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT
};
factorToGL = {
[ ZeroFactor ]: gl.ZERO,
[ OneFactor ]: gl.ONE,
[ SrcColorFactor ]: gl.SRC_COLOR,
[ SrcAlphaFactor ]: gl.SRC_ALPHA,
[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,
[ DstColorFactor ]: gl.DST_COLOR,
[ DstAlphaFactor ]: gl.DST_ALPHA,
[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,
[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,
[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,
[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA
};
const scissorParam = gl.getParameter( gl.SCISSOR_BOX );
const viewportParam = gl.getParameter( gl.VIEWPORT );
this.currentScissor = new Vector4().fromArray( scissorParam );
this.currentViewport = new Vector4().fromArray( viewportParam );
this._tempVec4 = new Vector4();
}
/**
* Enables the given WebGL capability.
*
* This method caches the capability state so
* `gl.enable()` is only called when necessary.
*
* @param {GLenum} id - The capability to enable.
*/
enable( id ) {
const { enabled } = this;
if ( enabled[ id ] !== true ) {
this.gl.enable( id );
enabled[ id ] = true;
}
}
/**
* Disables the given WebGL capability.
*
* This method caches the capability state so
* `gl.disable()` is only called when necessary.
*
* @param {GLenum} id - The capability to enable.
*/
disable( id ) {
const { enabled } = this;
if ( enabled[ id ] !== false ) {
this.gl.disable( id );
enabled[ id ] = false;
}
}
/**
* Specifies whether polygons are front- or back-facing
* by setting the winding orientation.
*
* This method caches the state so `gl.frontFace()` is only
* called when necessary.
*
* @param {Boolean} flipSided - Whether triangles flipped their sides or not.
*/
setFlipSided( flipSided ) {
if ( this.currentFlipSided !== flipSided ) {
const { gl } = this;
if ( flipSided ) {
gl.frontFace( gl.CW );
} else {
gl.frontFace( gl.CCW );
}
this.currentFlipSided = flipSided;
}
}
/**
* Specifies whether or not front- and/or back-facing
* polygons can be culled.
*
* This method caches the state so `gl.cullFace()` is only
* called when necessary.
*
* @param {Number} cullFace - Defines which polygons are candidates for culling.
*/
setCullFace( cullFace ) {
const { gl } = this;
if ( cullFace !== CullFaceNone ) {
this.enable( gl.CULL_FACE );
if ( cullFace !== this.currentCullFace ) {
if ( cullFace === CullFaceBack ) {
gl.cullFace( gl.BACK );
} else if ( cullFace === CullFaceFront ) {
gl.cullFace( gl.FRONT );
} else {
gl.cullFace( gl.FRONT_AND_BACK );
}
}
} else {
this.disable( gl.CULL_FACE );
}
this.currentCullFace = cullFace;
}
/**
* Specifies the width of line primitives.
*
* This method caches the state so `gl.lineWidth()` is only
* called when necessary.
*
* @param {Number} width - The line width.
*/
setLineWidth( width ) {
const { currentLineWidth, gl } = this;
if ( width !== currentLineWidth ) {
gl.lineWidth( width );
this.currentLineWidth = width;
}
}
/**
* Defines the blending.
*
* This method caches the state so `gl.blendEquation()`, `gl.blendEquationSeparate()`,
* `gl.blendFunc()` and `gl.blendFuncSeparate()` are only called when necessary.
*
* @param {Number} blending - The blending type.
* @param {Number} blendEquation - The blending equation.
* @param {Number} blendSrc - Only relevant for custom blending. The RGB source blending factor.
* @param {Number} blendDst - Only relevant for custom blending. The RGB destination blending factor.
* @param {Number} blendEquationAlpha - Only relevant for custom blending. The blending equation for alpha.
* @param {Number} blendSrcAlpha - Only relevant for custom blending. The alpha source blending factor.
* @param {Number} blendDstAlpha - Only relevant for custom blending. The alpha destination blending factor.
* @param {Boolean} premultipliedAlpha - Whether premultiplied alpha is enabled or not.
*/
setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
const { gl } = this;
if ( blending === NoBlending ) {
if ( this.currentBlendingEnabled === true ) {
this.disable( gl.BLEND );
this.currentBlendingEnabled = false;
}
return;
}
if ( this.currentBlendingEnabled === false ) {
this.enable( gl.BLEND );
this.currentBlendingEnabled = true;
}
if ( blending !== CustomBlending ) {
if ( blending !== this.currentBlending || premultipliedAlpha !== this.currentPremultipledAlpha ) {
if ( this.currentBlendEquation !== AddEquation || this.currentBlendEquationAlpha !== AddEquation ) {
gl.blendEquation( gl.FUNC_ADD );
this.currentBlendEquation = AddEquation;
this.currentBlendEquationAlpha = AddEquation;
}
if ( premultipliedAlpha ) {
switch ( blending ) {
case NormalBlending:
gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
break;
case AdditiveBlending:
gl.blendFunc( gl.ONE, gl.ONE );
break;
case SubtractiveBlending:
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
break;
case MultiplyBlending:
gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
break;
default:
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
break;
}
} else {
switch ( blending ) {
case NormalBlending:
gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
break;
case AdditiveBlending:
gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
break;
case SubtractiveBlending:
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
break;
case MultiplyBlending:
gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
break;
default:
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
break;
}
}
this.currentBlendSrc = null;
this.currentBlendDst = null;
this.currentBlendSrcAlpha = null;
this.currentBlendDstAlpha = null;
this.currentBlending = blending;
this.currentPremultipledAlpha = premultipliedAlpha;
}
return;
}
// custom blending
blendEquationAlpha = blendEquationAlpha || blendEquation;
blendSrcAlpha = blendSrcAlpha || blendSrc;
blendDstAlpha = blendDstAlpha || blendDst;
if ( blendEquation !== this.currentBlendEquation || blendEquationAlpha !== this.currentBlendEquationAlpha ) {
gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
this.currentBlendEquation = blendEquation;
this.currentBlendEquationAlpha = blendEquationAlpha;
}
if ( blendSrc !== this.currentBlendSrc || blendDst !== this.currentBlendDst || blendSrcAlpha !== this.currentBlendSrcAlpha || blendDstAlpha !== this.currentBlendDstAlpha ) {
gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
this.currentBlendSrc = blendSrc;
this.currentBlendDst = blendDst;
this.currentBlendSrcAlpha = blendSrcAlpha;
this.currentBlendDstAlpha = blendDstAlpha;
}
this.currentBlending = blending;
this.currentPremultipledAlpha = false;
}
/**
* Specifies whether colors can be written when rendering
* into a framebuffer or not.
*
* This method caches the state so `gl.colorMask()` is only
* called when necessary.
*
* @param {Boolean} colorMask - The color mask.
*/
setColorMask( colorMask ) {
if ( this.currentColorMask !== colorMask ) {
this.gl.colorMask( colorMask, colorMask, colorMask, colorMask );
this.currentColorMask = colorMask;
}
}
/**
* Specifies whether the depth test is enabled or not.
*
* @param {Boolean} depthTest - Whether the depth test is enabled or not.
*/
setDepthTest( depthTest ) {
const { gl } = this;
if ( depthTest ) {
this.enable( gl.DEPTH_TEST );
} else {
this.disable( gl.DEPTH_TEST );
}
}
/**
* Specifies whether depth values can be written when rendering
* into a framebuffer or not.
*
* This method caches the state so `gl.depthMask()` is only
* called when necessary.
*
* @param {Boolean} depthMask - The depth mask.
*/
setDepthMask( depthMask ) {
if ( this.currentDepthMask !== depthMask ) {
this.gl.depthMask( depthMask );
this.currentDepthMask = depthMask;
}
}
/**
* Specifies the depth compare function.
*
* This method caches the state so `gl.depthFunc()` is only
* called when necessary.
*
* @param {Number} depthFunc - The depth compare function.
*/
setDepthFunc( depthFunc ) {
if ( this.currentDepthFunc !== depthFunc ) {
const { gl } = this;
switch ( depthFunc ) {
case NeverDepth:
gl.depthFunc( gl.NEVER );
break;
case AlwaysDepth:
gl.depthFunc( gl.ALWAYS );
break;
case LessDepth:
gl.depthFunc( gl.LESS );
break;
case LessEqualDepth:
gl.depthFunc( gl.LEQUAL );
break;
case EqualDepth:
gl.depthFunc( gl.EQUAL );
break;
case GreaterEqualDepth:
gl.depthFunc( gl.GEQUAL );
break;
case GreaterDepth:
gl.depthFunc( gl.GREATER );
break;
case NotEqualDepth:
gl.depthFunc( gl.NOTEQUAL );
break;
default:
gl.depthFunc( gl.LEQUAL );
}
this.currentDepthFunc = depthFunc;
}
}
/**
* Specifies the viewport.
*
* @param {Number} x - The x-coordinate of the lower left corner of the viewport.
* @param {Number} y - The y-coordinate of the lower left corner of the viewport.
* @param {Number} width - The width of the viewport.
* @param {Number} height - The height of the viewport.
*
*/
scissor( x, y, width, height ) {
const scissor = this._tempVec4.set( x, y, width, height );
if ( this.currentScissor.equals( scissor ) === false ) {
const { gl } = this;
gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
this.currentScissor.copy( scissor );
}
}
/**
* Specifies the viewport.
*
* @param {Number} x - The x-coordinate of the lower left corner of the viewport.
* @param {Number} y - The y-coordinate of the lower left corner of the viewport.
* @param {Number} width - The width of the viewport.
* @param {Number} height - The height of the viewport.
*
*/
viewport( x, y, width, height ) {
const viewport = this._tempVec4.set( x, y, width, height );
if ( this.currentViewport.equals( viewport ) === false ) {
const { gl } = this;
gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
this.currentViewport.copy( viewport );
}
}
/**
* Defines the scissor test.
*
* @param {Boolean} boolean - Whether the scissor test should be enabled or not.
*/
setScissorTest( boolean ) {
const gl = this.gl;
if ( boolean ) {
gl.enable( gl.SCISSOR_TEST );
} else {
gl.disable( gl.SCISSOR_TEST );
}
}
/**
* Specifies whether the stencil test is enabled or not.
*
* @param {Boolean} stencilTest - Whether the stencil test is enabled or not.
*/
setStencilTest( stencilTest ) {
const { gl } = this;
if ( stencilTest ) {
this.enable( gl.STENCIL_TEST );
} else {
this.disable( gl.STENCIL_TEST );
}
}
/**
* Specifies whether stencil values can be written when rendering
* into a framebuffer or not.
*
* This method caches the state so `gl.stencilMask()` is only
* called when necessary.
*
* @param {Boolean} stencilMask - The stencil mask.
*/
setStencilMask( stencilMask ) {
if ( this.currentStencilMask !== stencilMask ) {
this.gl.stencilMask( stencilMask );
this.currentStencilMask = stencilMask;
}
}
/**
* Specifies whether the stencil test functions.
*
* This method caches the state so `gl.stencilFunc()` is only
* called when necessary.
*
* @param {Number} stencilFunc - The stencil compare function.
* @param {Number} stencilRef - The reference value for the stencil test.
* @param {Number} stencilMask - A bit-wise mask that is used to AND the reference value and the stored stencil value when the test is done.
*/
setStencilFunc( stencilFunc, stencilRef, stencilMask ) {
if ( this.currentStencilFunc !== stencilFunc ||
this.currentStencilRef !== stencilRef ||
this.currentStencilFuncMask !== stencilMask ) {
this.gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
this.currentStencilFunc = stencilFunc;
this.currentStencilRef = stencilRef;
this.currentStencilFuncMask = stencilMask;
}
}
/**
* Specifies whether the stencil test operation.
*
* This method caches the state so `gl.stencilOp()` is only
* called when necessary.
*
* @param {Number} stencilFail - The function to use when the stencil test fails.
* @param {Number} stencilZFail - The function to use when the stencil test passes, but the depth test fail.
* @param {Number} stencilZPass - The function to use when both the stencil test and the depth test pass,
* or when the stencil test passes and there is no depth buffer or depth testing is disabled.
*/
setStencilOp( stencilFail, stencilZFail, stencilZPass ) {
if ( this.currentStencilFail !== stencilFail ||
this.currentStencilZFail !== stencilZFail ||
this.currentStencilZPass !== stencilZPass ) {
this.gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
this.currentStencilFail = stencilFail;
this.currentStencilZFail = stencilZFail;
this.currentStencilZPass = stencilZPass;
}
}
/**
* Configures the WebGL state for the given material.
*
* @param {Material} material - The material to configure the state for.
* @param {Number} frontFaceCW - Whether the front faces are counter-clockwise or not.
* @param {Number} hardwareClippingPlanes - The number of hardware clipping planes.
*/
setMaterial( material, frontFaceCW, hardwareClippingPlanes ) {
const { gl } = this;
material.side === DoubleSide
? this.disable( gl.CULL_FACE )
: this.enable( gl.CULL_FACE );
let flipSided = ( material.side === BackSide );
if ( frontFaceCW ) flipSided = ! flipSided;
this.setFlipSided( flipSided );
( material.blending === NormalBlending && material.transparent === false )
? this.setBlending( NoBlending )
: this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
this.setDepthFunc( material.depthFunc );
this.setDepthTest( material.depthTest );
this.setDepthMask( material.depthWrite );
this.setColorMask( material.colorWrite );
const stencilWrite = material.stencilWrite;
this.setStencilTest( stencilWrite );
if ( stencilWrite ) {
this.setStencilMask( material.stencilWriteMask );
this.setStencilFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
this.setStencilOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
}
this.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
material.alphaToCoverage === true && this.backend.renderer.samples > 1
? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
: this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
if ( hardwareClippingPlanes > 0 ) {
if ( this.currentClippingPlanes !== hardwareClippingPlanes ) {
const CLIP_DISTANCE0_WEBGL = 0x3000;
for ( let i = 0; i < 8; i ++ ) {
if ( i < hardwareClippingPlanes ) {
this.enable( CLIP_DISTANCE0_WEBGL + i );
} else {
this.disable( CLIP_DISTANCE0_WEBGL + i );
}
}
}
}
}
/**
* Specifies the polygon offset.
*
* This method caches the state so `gl.polygonOffset()` is only
* called when necessary.
*
* @param {Boolean} polygonOffset - Whether polygon offset is enabled or not.
* @param {Number} factor - The scale factor for the variable depth offset for each polygon.
* @param {Number} units - The multiplier by which an implementation-specific value is multiplied with to create a constant depth offset.
*/
setPolygonOffset( polygonOffset, factor, units ) {
const { gl } = this;
if ( polygonOffset ) {
this.enable( gl.POLYGON_OFFSET_FILL );
if ( this.currentPolygonOffsetFactor !== factor || this.currentPolygonOffsetUnits !== units ) {
gl.polygonOffset( factor, units );
this.currentPolygonOffsetFactor = factor;
this.currentPolygonOffsetUnits = units;
}
} else {
this.disable( gl.POLYGON_OFFSET_FILL );
}
}
/**
* Defines the usage of the given WebGL program.
*
* This method caches the state so `gl.useProgram()` is only
* called when necessary.
*
* @param {WebGLProgram} program - The WebGL program to use.
* @return {Boolean} Whether a program change has been executed or not.
*/
useProgram( program ) {
if ( this.currentProgram !== program ) {
this.gl.useProgram( program );
this.currentProgram = program;
return true;
}
return false;
}
// framebuffer
/**
* Binds the given framebuffer.
*
* This method caches the state so `gl.bindFramebuffer()` is only
* called when necessary.
*
* @param {Number} target - The binding point (target).
* @param {WebGLFramebuffer} framebuffer - The WebGL framebuffer to bind.
* @return {Boolean} Whether a bind has been executed or not.
*/
bindFramebuffer( target, framebuffer ) {
const { gl, currentBoundFramebuffers } = this;
if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
gl.bindFramebuffer( target, framebuffer );
currentBoundFramebuffers[ target ] = framebuffer;
// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
if ( target === gl.DRAW_FRAMEBUFFER ) {
currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;
}
if ( target === gl.FRAMEBUFFER ) {
currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;
}
return true;
}
return false;
}
/**
* Defines draw buffers to which fragment colors are written into.
* Configures the MRT setup of custom framebuffers.
*
* This method caches the state so `gl.drawBuffers()` is only
* called when necessary.
*
* @param {RenderContext} renderContext - The render context.
* @param {WebGLFramebuffer} framebuffer - The WebGL framebuffer.
*/
drawBuffers( renderContext, framebuffer ) {
const { gl } = this;
let drawBuffers = [];
let needsUpdate = false;
if ( renderContext.textures !== null ) {
drawBuffers = this.currentDrawbuffers.get( framebuffer );
if ( drawBuffers === undefined ) {
drawBuffers = [];
this.currentDrawbuffers.set( framebuffer, drawBuffers );
}
const textures = renderContext.textures;
if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {
for ( let i = 0, il = textures.length; i < il; i ++ ) {
drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;
}
drawBuffers.length = textures.length;
needsUpdate = true;
}
} else {
if ( drawBuffers[ 0 ] !== gl.BACK ) {
drawBuffers[ 0 ] = gl.BACK;
needsUpdate = true;
}
}
if ( needsUpdate ) {
gl.drawBuffers( drawBuffers );
}
}
// texture
/**
* Makes the given texture unit active.
*
* This method caches the state so `gl.activeTexture()` is only
* called when necessary.
*
* @param {Number} webglSlot - The texture unit to make active.
*/
activeTexture( webglSlot ) {
const { gl, currentTextureSlot, maxTextures } = this;
if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
if ( currentTextureSlot !== webglSlot ) {
gl.activeTexture( webglSlot );
this.currentTextureSlot = webglSlot;
}
}
/**
* Binds the given WebGL texture to a target.
*
* This method caches the state so `gl.bindTexture()` is only
* called when necessary.
*
* @param {Number} webglType - The binding point (target).
* @param {WebGLTexture} webglTexture - The WebGL texture to bind.
* @param {Number} webglSlot - The texture.
*/
bindTexture( webglType, webglTexture, webglSlot ) {
const { gl, currentTextureSlot, currentBoundTextures, maxTextures } = this;
if ( webglSlot === undefined ) {
if ( currentTextureSlot === null ) {
webglSlot = gl.TEXTURE0 + maxTextures - 1;
} else {
webglSlot = currentTextureSlot;
}
}
let boundTexture = currentBoundTextures[ webglSlot ];
if ( boundTexture === undefined ) {
boundTexture = { type: undefined, texture: undefined };
currentBoundTextures[ webglSlot ] = boundTexture;
}
if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
if ( currentTextureSlot !== webglSlot ) {
gl.activeTexture( webglSlot );
this.currentTextureSlot = webglSlot;
}
gl.bindTexture( webglType, webglTexture );
boundTexture.type = webglType;
boundTexture.texture = webglTexture;
}
}
/**
* Binds a given WebGL buffer to a given binding point (target) at a given index.
*
* This method caches the state so `gl.bindBufferBase()` is only
* called when necessary.
*
* @param {Number} target - The target for the bind operation.
* @param {Number} index - The index of the target.
* @param {WebGLBuffer} buffer - The WebGL buffer.
* @return {Boolean} Whether a bind has been executed or not.
*/
bindBufferBase( target, index, buffer ) {
const { gl } = this;
const key = `${target}-${index}`;
if ( this.currentBoundBufferBases[ key ] !== buffer ) {
gl.bindBufferBase( target, index, buffer );
this.currentBoundBufferBases[ key ] = buffer;
return true;
}
return false;
}
/**
* Unbinds the current bound texture.
*
* This method caches the state so `gl.bindTexture()` is only
* called when necessary.
*/
unbindTexture() {
const { gl, currentTextureSlot, currentBoundTextures } = this;
const boundTexture = currentBoundTextures[ currentTextureSlot ];
if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
gl.bindTexture( boundTexture.type, null );
boundTexture.type = undefined;
boundTexture.texture = undefined;
}
}
}
export default WebGLState;