Buckets:
| 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.currentVAO = null; | |
| this.currentIndex = null; | |
| 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 scissor box. | |
| * | |
| * @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; | |
| } | |
| /** | |
| * Sets the vertex state by binding the given VAO and element buffer. | |
| * | |
| * @param {WebGLVertexArrayObject} vao - The VAO. | |
| * @param {WebGLBuffer} indexBuffer - The index buffer. | |
| * @return {boolean} Whether a vertex state has been changed or not. | |
| */ | |
| setVertexState( vao, indexBuffer = null ) { | |
| const gl = this.gl; | |
| if ( this.currentVAO !== vao || this.currentIndex !== indexBuffer ) { | |
| gl.bindVertexArray( vao ); | |
| if ( indexBuffer !== null ) { | |
| gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, indexBuffer ); | |
| } | |
| this.currentVAO = vao; | |
| this.currentIndex = indexBuffer; | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Resets the vertex array state by resetting the VAO and element buffer. | |
| */ | |
| resetVertexState() { | |
| const gl = this.gl; | |
| gl.bindVertexArray( null ); | |
| gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); | |
| this.currentVAO = null; | |
| this.currentIndex = null; | |
| } | |
| // 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; | |
Xet Storage Details
- Size:
- 26.4 kB
- Xet hash:
- b4ad63eb8f9bfeb311b253a235449e327d6c8ff754df6e1ad6026548ff097e85
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.