Spaces:
Running
Running
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, DoubleSide, BackSide } from '../../constants.js'; | |
| import { Vector4 } from '../../math/Vector4.js'; | |
| function WebGLState( gl, extensions, utils, capabilities ) { | |
| function ColorBuffer() { | |
| var locked = false; | |
| var color = new Vector4(); | |
| var currentColorMask = null; | |
| var currentColorClear = new Vector4( 0, 0, 0, 0 ); | |
| return { | |
| setMask: function ( colorMask ) { | |
| if ( currentColorMask !== colorMask && ! locked ) { | |
| gl.colorMask( colorMask, colorMask, colorMask, colorMask ); | |
| currentColorMask = colorMask; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( r, g, b, a, premultipliedAlpha ) { | |
| if ( premultipliedAlpha === true ) { | |
| r *= a; g *= a; b *= a; | |
| } | |
| color.set( r, g, b, a ); | |
| if ( currentColorClear.equals( color ) === false ) { | |
| gl.clearColor( r, g, b, a ); | |
| currentColorClear.copy( color ); | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentColorMask = null; | |
| currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state | |
| } | |
| }; | |
| } | |
| function DepthBuffer() { | |
| var locked = false; | |
| var currentDepthMask = null; | |
| var currentDepthFunc = null; | |
| var currentDepthClear = null; | |
| return { | |
| setTest: function ( depthTest ) { | |
| if ( depthTest ) { | |
| enable( gl.DEPTH_TEST ); | |
| } else { | |
| disable( gl.DEPTH_TEST ); | |
| } | |
| }, | |
| setMask: function ( depthMask ) { | |
| if ( currentDepthMask !== depthMask && ! locked ) { | |
| gl.depthMask( depthMask ); | |
| currentDepthMask = depthMask; | |
| } | |
| }, | |
| setFunc: function ( depthFunc ) { | |
| if ( currentDepthFunc !== depthFunc ) { | |
| if ( depthFunc ) { | |
| 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 ); | |
| } | |
| } else { | |
| gl.depthFunc( gl.LEQUAL ); | |
| } | |
| currentDepthFunc = depthFunc; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( depth ) { | |
| if ( currentDepthClear !== depth ) { | |
| gl.clearDepth( depth ); | |
| currentDepthClear = depth; | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentDepthMask = null; | |
| currentDepthFunc = null; | |
| currentDepthClear = null; | |
| } | |
| }; | |
| } | |
| function StencilBuffer() { | |
| var locked = false; | |
| var currentStencilMask = null; | |
| var currentStencilFunc = null; | |
| var currentStencilRef = null; | |
| var currentStencilFuncMask = null; | |
| var currentStencilFail = null; | |
| var currentStencilZFail = null; | |
| var currentStencilZPass = null; | |
| var currentStencilClear = null; | |
| return { | |
| setTest: function ( stencilTest ) { | |
| if ( stencilTest ) { | |
| enable( gl.STENCIL_TEST ); | |
| } else { | |
| disable( gl.STENCIL_TEST ); | |
| } | |
| }, | |
| setMask: function ( stencilMask ) { | |
| if ( currentStencilMask !== stencilMask && ! locked ) { | |
| gl.stencilMask( stencilMask ); | |
| currentStencilMask = stencilMask; | |
| } | |
| }, | |
| setFunc: function ( stencilFunc, stencilRef, stencilMask ) { | |
| if ( currentStencilFunc !== stencilFunc || | |
| currentStencilRef !== stencilRef || | |
| currentStencilFuncMask !== stencilMask ) { | |
| gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); | |
| currentStencilFunc = stencilFunc; | |
| currentStencilRef = stencilRef; | |
| currentStencilFuncMask = stencilMask; | |
| } | |
| }, | |
| setOp: function ( stencilFail, stencilZFail, stencilZPass ) { | |
| if ( currentStencilFail !== stencilFail || | |
| currentStencilZFail !== stencilZFail || | |
| currentStencilZPass !== stencilZPass ) { | |
| gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); | |
| currentStencilFail = stencilFail; | |
| currentStencilZFail = stencilZFail; | |
| currentStencilZPass = stencilZPass; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( stencil ) { | |
| if ( currentStencilClear !== stencil ) { | |
| gl.clearStencil( stencil ); | |
| currentStencilClear = stencil; | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentStencilMask = null; | |
| currentStencilFunc = null; | |
| currentStencilRef = null; | |
| currentStencilFuncMask = null; | |
| currentStencilFail = null; | |
| currentStencilZFail = null; | |
| currentStencilZPass = null; | |
| currentStencilClear = null; | |
| } | |
| }; | |
| } | |
| // | |
| var colorBuffer = new ColorBuffer(); | |
| var depthBuffer = new DepthBuffer(); | |
| var stencilBuffer = new StencilBuffer(); | |
| var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); | |
| var newAttributes = new Uint8Array( maxVertexAttributes ); | |
| var enabledAttributes = new Uint8Array( maxVertexAttributes ); | |
| var attributeDivisors = new Uint8Array( maxVertexAttributes ); | |
| var enabledCapabilities = {}; | |
| var compressedTextureFormats = null; | |
| var currentProgram = null; | |
| var currentBlendingEnabled = null; | |
| var currentBlending = null; | |
| var currentBlendEquation = null; | |
| var currentBlendSrc = null; | |
| var currentBlendDst = null; | |
| var currentBlendEquationAlpha = null; | |
| var currentBlendSrcAlpha = null; | |
| var currentBlendDstAlpha = null; | |
| var currentPremultipledAlpha = false; | |
| var currentFlipSided = null; | |
| var currentCullFace = null; | |
| var currentLineWidth = null; | |
| var currentPolygonOffsetFactor = null; | |
| var currentPolygonOffsetUnits = null; | |
| var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); | |
| var lineWidthAvailable = false; | |
| var version = 0; | |
| var glVersion = gl.getParameter( gl.VERSION ); | |
| if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { | |
| version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); | |
| lineWidthAvailable = ( version >= 1.0 ); | |
| } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { | |
| version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); | |
| lineWidthAvailable = ( version >= 2.0 ); | |
| } | |
| var currentTextureSlot = null; | |
| var currentBoundTextures = {}; | |
| var currentScissor = new Vector4(); | |
| var currentViewport = new Vector4(); | |
| function createTexture( type, target, count ) { | |
| var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. | |
| var texture = gl.createTexture(); | |
| gl.bindTexture( type, texture ); | |
| gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); | |
| gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); | |
| for ( var i = 0; i < count; i ++ ) { | |
| gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); | |
| } | |
| return texture; | |
| } | |
| var emptyTextures = {}; | |
| emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); | |
| emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); | |
| // init | |
| colorBuffer.setClear( 0, 0, 0, 1 ); | |
| depthBuffer.setClear( 1 ); | |
| stencilBuffer.setClear( 0 ); | |
| enable( gl.DEPTH_TEST ); | |
| depthBuffer.setFunc( LessEqualDepth ); | |
| setFlipSided( false ); | |
| setCullFace( CullFaceBack ); | |
| enable( gl.CULL_FACE ); | |
| setBlending( NoBlending ); | |
| // | |
| function initAttributes() { | |
| for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { | |
| newAttributes[ i ] = 0; | |
| } | |
| } | |
| function enableAttribute( attribute ) { | |
| enableAttributeAndDivisor( attribute, 0 ); | |
| } | |
| function enableAttributeAndDivisor( attribute, meshPerAttribute ) { | |
| newAttributes[ attribute ] = 1; | |
| if ( enabledAttributes[ attribute ] === 0 ) { | |
| gl.enableVertexAttribArray( attribute ); | |
| enabledAttributes[ attribute ] = 1; | |
| } | |
| if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { | |
| var extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); | |
| extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); | |
| attributeDivisors[ attribute ] = meshPerAttribute; | |
| } | |
| } | |
| function disableUnusedAttributes() { | |
| for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { | |
| if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { | |
| gl.disableVertexAttribArray( i ); | |
| enabledAttributes[ i ] = 0; | |
| } | |
| } | |
| } | |
| function enable( id ) { | |
| if ( enabledCapabilities[ id ] !== true ) { | |
| gl.enable( id ); | |
| enabledCapabilities[ id ] = true; | |
| } | |
| } | |
| function disable( id ) { | |
| if ( enabledCapabilities[ id ] !== false ) { | |
| gl.disable( id ); | |
| enabledCapabilities[ id ] = false; | |
| } | |
| } | |
| function getCompressedTextureFormats() { | |
| if ( compressedTextureFormats === null ) { | |
| compressedTextureFormats = []; | |
| if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || | |
| extensions.get( 'WEBGL_compressed_texture_s3tc' ) || | |
| extensions.get( 'WEBGL_compressed_texture_etc1' ) || | |
| extensions.get( 'WEBGL_compressed_texture_astc' ) ) { | |
| var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); | |
| for ( var i = 0; i < formats.length; i ++ ) { | |
| compressedTextureFormats.push( formats[ i ] ); | |
| } | |
| } | |
| } | |
| return compressedTextureFormats; | |
| } | |
| function useProgram( program ) { | |
| if ( currentProgram !== program ) { | |
| gl.useProgram( program ); | |
| currentProgram = program; | |
| return true; | |
| } | |
| return false; | |
| } | |
| function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { | |
| if ( blending === NoBlending ) { | |
| if ( currentBlendingEnabled ) { | |
| disable( gl.BLEND ); | |
| currentBlendingEnabled = false; | |
| } | |
| return; | |
| } | |
| if ( ! currentBlendingEnabled ) { | |
| enable( gl.BLEND ); | |
| currentBlendingEnabled = true; | |
| } | |
| if ( blending !== CustomBlending ) { | |
| if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { | |
| if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { | |
| gl.blendEquation( gl.FUNC_ADD ); | |
| currentBlendEquation = AddEquation; | |
| 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.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); | |
| 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.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); | |
| break; | |
| case MultiplyBlending: | |
| gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); | |
| break; | |
| default: | |
| console.error( 'THREE.WebGLState: Invalid blending: ', blending ); | |
| break; | |
| } | |
| } | |
| currentBlendSrc = null; | |
| currentBlendDst = null; | |
| currentBlendSrcAlpha = null; | |
| currentBlendDstAlpha = null; | |
| currentBlending = blending; | |
| currentPremultipledAlpha = premultipliedAlpha; | |
| } | |
| return; | |
| } | |
| // custom blending | |
| blendEquationAlpha = blendEquationAlpha || blendEquation; | |
| blendSrcAlpha = blendSrcAlpha || blendSrc; | |
| blendDstAlpha = blendDstAlpha || blendDst; | |
| if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { | |
| gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); | |
| currentBlendEquation = blendEquation; | |
| currentBlendEquationAlpha = blendEquationAlpha; | |
| } | |
| if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { | |
| gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); | |
| currentBlendSrc = blendSrc; | |
| currentBlendDst = blendDst; | |
| currentBlendSrcAlpha = blendSrcAlpha; | |
| currentBlendDstAlpha = blendDstAlpha; | |
| } | |
| currentBlending = blending; | |
| currentPremultipledAlpha = null; | |
| } | |
| function setMaterial( material, frontFaceCW ) { | |
| material.side === DoubleSide | |
| ? disable( gl.CULL_FACE ) | |
| : enable( gl.CULL_FACE ); | |
| var flipSided = ( material.side === BackSide ); | |
| if ( frontFaceCW ) flipSided = ! flipSided; | |
| setFlipSided( flipSided ); | |
| ( material.blending === NormalBlending && material.transparent === false ) | |
| ? setBlending( NoBlending ) | |
| : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); | |
| depthBuffer.setFunc( material.depthFunc ); | |
| depthBuffer.setTest( material.depthTest ); | |
| depthBuffer.setMask( material.depthWrite ); | |
| colorBuffer.setMask( material.colorWrite ); | |
| setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); | |
| } | |
| // | |
| function setFlipSided( flipSided ) { | |
| if ( currentFlipSided !== flipSided ) { | |
| if ( flipSided ) { | |
| gl.frontFace( gl.CW ); | |
| } else { | |
| gl.frontFace( gl.CCW ); | |
| } | |
| currentFlipSided = flipSided; | |
| } | |
| } | |
| function setCullFace( cullFace ) { | |
| if ( cullFace !== CullFaceNone ) { | |
| enable( gl.CULL_FACE ); | |
| if ( cullFace !== currentCullFace ) { | |
| if ( cullFace === CullFaceBack ) { | |
| gl.cullFace( gl.BACK ); | |
| } else if ( cullFace === CullFaceFront ) { | |
| gl.cullFace( gl.FRONT ); | |
| } else { | |
| gl.cullFace( gl.FRONT_AND_BACK ); | |
| } | |
| } | |
| } else { | |
| disable( gl.CULL_FACE ); | |
| } | |
| currentCullFace = cullFace; | |
| } | |
| function setLineWidth( width ) { | |
| if ( width !== currentLineWidth ) { | |
| if ( lineWidthAvailable ) gl.lineWidth( width ); | |
| currentLineWidth = width; | |
| } | |
| } | |
| function setPolygonOffset( polygonOffset, factor, units ) { | |
| if ( polygonOffset ) { | |
| enable( gl.POLYGON_OFFSET_FILL ); | |
| if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { | |
| gl.polygonOffset( factor, units ); | |
| currentPolygonOffsetFactor = factor; | |
| currentPolygonOffsetUnits = units; | |
| } | |
| } else { | |
| disable( gl.POLYGON_OFFSET_FILL ); | |
| } | |
| } | |
| function setScissorTest( scissorTest ) { | |
| if ( scissorTest ) { | |
| enable( gl.SCISSOR_TEST ); | |
| } else { | |
| disable( gl.SCISSOR_TEST ); | |
| } | |
| } | |
| // texture | |
| function activeTexture( webglSlot ) { | |
| if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; | |
| if ( currentTextureSlot !== webglSlot ) { | |
| gl.activeTexture( webglSlot ); | |
| currentTextureSlot = webglSlot; | |
| } | |
| } | |
| function bindTexture( webglType, webglTexture ) { | |
| if ( currentTextureSlot === null ) { | |
| activeTexture(); | |
| } | |
| var boundTexture = currentBoundTextures[ currentTextureSlot ]; | |
| if ( boundTexture === undefined ) { | |
| boundTexture = { type: undefined, texture: undefined }; | |
| currentBoundTextures[ currentTextureSlot ] = boundTexture; | |
| } | |
| if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { | |
| gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); | |
| boundTexture.type = webglType; | |
| boundTexture.texture = webglTexture; | |
| } | |
| } | |
| function compressedTexImage2D() { | |
| try { | |
| gl.compressedTexImage2D.apply( gl, arguments ); | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLState:', error ); | |
| } | |
| } | |
| function texImage2D() { | |
| try { | |
| gl.texImage2D.apply( gl, arguments ); | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLState:', error ); | |
| } | |
| } | |
| function texImage3D() { | |
| try { | |
| gl.texImage3D.apply( gl, arguments ); | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLState:', error ); | |
| } | |
| } | |
| // | |
| function scissor( scissor ) { | |
| if ( currentScissor.equals( scissor ) === false ) { | |
| gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); | |
| currentScissor.copy( scissor ); | |
| } | |
| } | |
| function viewport( viewport ) { | |
| if ( currentViewport.equals( viewport ) === false ) { | |
| gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); | |
| currentViewport.copy( viewport ); | |
| } | |
| } | |
| // | |
| function reset() { | |
| for ( var i = 0; i < enabledAttributes.length; i ++ ) { | |
| if ( enabledAttributes[ i ] === 1 ) { | |
| gl.disableVertexAttribArray( i ); | |
| enabledAttributes[ i ] = 0; | |
| } | |
| } | |
| enabledCapabilities = {}; | |
| compressedTextureFormats = null; | |
| currentTextureSlot = null; | |
| currentBoundTextures = {}; | |
| currentProgram = null; | |
| currentBlending = null; | |
| currentFlipSided = null; | |
| currentCullFace = null; | |
| colorBuffer.reset(); | |
| depthBuffer.reset(); | |
| stencilBuffer.reset(); | |
| } | |
| return { | |
| buffers: { | |
| color: colorBuffer, | |
| depth: depthBuffer, | |
| stencil: stencilBuffer | |
| }, | |
| initAttributes: initAttributes, | |
| enableAttribute: enableAttribute, | |
| enableAttributeAndDivisor: enableAttributeAndDivisor, | |
| disableUnusedAttributes: disableUnusedAttributes, | |
| enable: enable, | |
| disable: disable, | |
| getCompressedTextureFormats: getCompressedTextureFormats, | |
| useProgram: useProgram, | |
| setBlending: setBlending, | |
| setMaterial: setMaterial, | |
| setFlipSided: setFlipSided, | |
| setCullFace: setCullFace, | |
| setLineWidth: setLineWidth, | |
| setPolygonOffset: setPolygonOffset, | |
| setScissorTest: setScissorTest, | |
| activeTexture: activeTexture, | |
| bindTexture: bindTexture, | |
| compressedTexImage2D: compressedTexImage2D, | |
| texImage2D: texImage2D, | |
| texImage3D: texImage3D, | |
| scissor: scissor, | |
| viewport: viewport, | |
| reset: reset | |
| }; | |
| } | |
| export { WebGLState }; | |