2 * @author mrdoob / http://mrdoob.com/
5 import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, DoubleSide, BackSide } from '../../constants';
6 import { Vector4 } from '../../math/Vector4';
8 function WebGLState( gl, extensions, utils ) {
10 function ColorBuffer() {
14 var color = new Vector4();
15 var currentColorMask = null;
16 var currentColorClear = new Vector4( 0, 0, 0, 0 );
20 setMask: function ( colorMask ) {
22 if ( currentColorMask !== colorMask && ! locked ) {
24 gl.colorMask( colorMask, colorMask, colorMask, colorMask );
25 currentColorMask = colorMask;
31 setLocked: function ( lock ) {
37 setClear: function ( r, g, b, a, premultipliedAlpha ) {
39 if ( premultipliedAlpha === true ) {
41 r *= a; g *= a; b *= a;
45 color.set( r, g, b, a );
47 if ( currentColorClear.equals( color ) === false ) {
49 gl.clearColor( r, g, b, a );
50 currentColorClear.copy( color );
60 currentColorMask = null;
61 currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state
69 function DepthBuffer() {
73 var currentDepthMask = null;
74 var currentDepthFunc = null;
75 var currentDepthClear = null;
79 setTest: function ( depthTest ) {
83 enable( gl.DEPTH_TEST );
87 disable( gl.DEPTH_TEST );
93 setMask: function ( depthMask ) {
95 if ( currentDepthMask !== depthMask && ! locked ) {
97 gl.depthMask( depthMask );
98 currentDepthMask = depthMask;
104 setFunc: function ( depthFunc ) {
106 if ( currentDepthFunc !== depthFunc ) {
110 switch ( depthFunc ) {
114 gl.depthFunc( gl.NEVER );
119 gl.depthFunc( gl.ALWAYS );
124 gl.depthFunc( gl.LESS );
129 gl.depthFunc( gl.LEQUAL );
134 gl.depthFunc( gl.EQUAL );
137 case GreaterEqualDepth:
139 gl.depthFunc( gl.GEQUAL );
144 gl.depthFunc( gl.GREATER );
149 gl.depthFunc( gl.NOTEQUAL );
154 gl.depthFunc( gl.LEQUAL );
160 gl.depthFunc( gl.LEQUAL );
164 currentDepthFunc = depthFunc;
170 setLocked: function ( lock ) {
176 setClear: function ( depth ) {
178 if ( currentDepthClear !== depth ) {
180 gl.clearDepth( depth );
181 currentDepthClear = depth;
191 currentDepthMask = null;
192 currentDepthFunc = null;
193 currentDepthClear = null;
201 function StencilBuffer() {
205 var currentStencilMask = null;
206 var currentStencilFunc = null;
207 var currentStencilRef = null;
208 var currentStencilFuncMask = null;
209 var currentStencilFail = null;
210 var currentStencilZFail = null;
211 var currentStencilZPass = null;
212 var currentStencilClear = null;
216 setTest: function ( stencilTest ) {
220 enable( gl.STENCIL_TEST );
224 disable( gl.STENCIL_TEST );
230 setMask: function ( stencilMask ) {
232 if ( currentStencilMask !== stencilMask && ! locked ) {
234 gl.stencilMask( stencilMask );
235 currentStencilMask = stencilMask;
241 setFunc: function ( stencilFunc, stencilRef, stencilMask ) {
243 if ( currentStencilFunc !== stencilFunc ||
244 currentStencilRef !== stencilRef ||
245 currentStencilFuncMask !== stencilMask ) {
247 gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
249 currentStencilFunc = stencilFunc;
250 currentStencilRef = stencilRef;
251 currentStencilFuncMask = stencilMask;
257 setOp: function ( stencilFail, stencilZFail, stencilZPass ) {
259 if ( currentStencilFail !== stencilFail ||
260 currentStencilZFail !== stencilZFail ||
261 currentStencilZPass !== stencilZPass ) {
263 gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
265 currentStencilFail = stencilFail;
266 currentStencilZFail = stencilZFail;
267 currentStencilZPass = stencilZPass;
273 setLocked: function ( lock ) {
279 setClear: function ( stencil ) {
281 if ( currentStencilClear !== stencil ) {
283 gl.clearStencil( stencil );
284 currentStencilClear = stencil;
294 currentStencilMask = null;
295 currentStencilFunc = null;
296 currentStencilRef = null;
297 currentStencilFuncMask = null;
298 currentStencilFail = null;
299 currentStencilZFail = null;
300 currentStencilZPass = null;
301 currentStencilClear = null;
311 var colorBuffer = new ColorBuffer();
312 var depthBuffer = new DepthBuffer();
313 var stencilBuffer = new StencilBuffer();
315 var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
316 var newAttributes = new Uint8Array( maxVertexAttributes );
317 var enabledAttributes = new Uint8Array( maxVertexAttributes );
318 var attributeDivisors = new Uint8Array( maxVertexAttributes );
320 var capabilities = {};
322 var compressedTextureFormats = null;
324 var currentProgram = null;
326 var currentBlending = null;
327 var currentBlendEquation = null;
328 var currentBlendSrc = null;
329 var currentBlendDst = null;
330 var currentBlendEquationAlpha = null;
331 var currentBlendSrcAlpha = null;
332 var currentBlendDstAlpha = null;
333 var currentPremultipledAlpha = false;
335 var currentFlipSided = null;
336 var currentCullFace = null;
338 var currentLineWidth = null;
340 var currentPolygonOffsetFactor = null;
341 var currentPolygonOffsetUnits = null;
343 var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );
345 var version = parseFloat( /^WebGL\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] );
346 var lineWidthAvailable = parseFloat( version ) >= 1.0;
348 var currentTextureSlot = null;
349 var currentBoundTextures = {};
351 var currentScissor = new Vector4();
352 var currentViewport = new Vector4();
354 function createTexture( type, target, count ) {
356 var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.
357 var texture = gl.createTexture();
359 gl.bindTexture( type, texture );
360 gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
361 gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
363 for ( var i = 0; i < count; i ++ ) {
365 gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
373 var emptyTextures = {};
374 emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );
375 emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );
379 colorBuffer.setClear( 0, 0, 0, 1 );
380 depthBuffer.setClear( 1 );
381 stencilBuffer.setClear( 0 );
383 enable( gl.DEPTH_TEST );
384 depthBuffer.setFunc( LessEqualDepth );
386 setFlipSided( false );
387 setCullFace( CullFaceBack );
388 enable( gl.CULL_FACE );
391 setBlending( NormalBlending );
395 function initAttributes() {
397 for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
399 newAttributes[ i ] = 0;
405 function enableAttribute( attribute ) {
407 newAttributes[ attribute ] = 1;
409 if ( enabledAttributes[ attribute ] === 0 ) {
411 gl.enableVertexAttribArray( attribute );
412 enabledAttributes[ attribute ] = 1;
416 if ( attributeDivisors[ attribute ] !== 0 ) {
418 var extension = extensions.get( 'ANGLE_instanced_arrays' );
420 extension.vertexAttribDivisorANGLE( attribute, 0 );
421 attributeDivisors[ attribute ] = 0;
427 function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
429 newAttributes[ attribute ] = 1;
431 if ( enabledAttributes[ attribute ] === 0 ) {
433 gl.enableVertexAttribArray( attribute );
434 enabledAttributes[ attribute ] = 1;
438 if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
440 var extension = extensions.get( 'ANGLE_instanced_arrays' );
442 extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
443 attributeDivisors[ attribute ] = meshPerAttribute;
449 function disableUnusedAttributes() {
451 for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {
453 if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
455 gl.disableVertexAttribArray( i );
456 enabledAttributes[ i ] = 0;
464 function enable( id ) {
466 if ( capabilities[ id ] !== true ) {
469 capabilities[ id ] = true;
475 function disable( id ) {
477 if ( capabilities[ id ] !== false ) {
480 capabilities[ id ] = false;
486 function getCompressedTextureFormats() {
488 if ( compressedTextureFormats === null ) {
490 compressedTextureFormats = [];
492 if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
493 extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
494 extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {
496 var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
498 for ( var i = 0; i < formats.length; i ++ ) {
500 compressedTextureFormats.push( formats[ i ] );
508 return compressedTextureFormats;
512 function useProgram( program ) {
514 if ( currentProgram !== program ) {
516 gl.useProgram( program );
518 currentProgram = program;
528 function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
530 if ( blending !== NoBlending ) {
540 if ( blending !== CustomBlending ) {
542 if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {
544 switch ( blending ) {
546 case AdditiveBlending:
548 if ( premultipliedAlpha ) {
550 gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
551 gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE );
555 gl.blendEquation( gl.FUNC_ADD );
556 gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
561 case SubtractiveBlending:
563 if ( premultipliedAlpha ) {
565 gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
566 gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA );
570 gl.blendEquation( gl.FUNC_ADD );
571 gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );
576 case MultiplyBlending:
578 if ( premultipliedAlpha ) {
580 gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
581 gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
585 gl.blendEquation( gl.FUNC_ADD );
586 gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
593 if ( premultipliedAlpha ) {
595 gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
596 gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
600 gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
601 gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
609 currentBlendEquation = null;
610 currentBlendSrc = null;
611 currentBlendDst = null;
612 currentBlendEquationAlpha = null;
613 currentBlendSrcAlpha = null;
614 currentBlendDstAlpha = null;
618 blendEquationAlpha = blendEquationAlpha || blendEquation;
619 blendSrcAlpha = blendSrcAlpha || blendSrc;
620 blendDstAlpha = blendDstAlpha || blendDst;
622 if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
624 gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) );
626 currentBlendEquation = blendEquation;
627 currentBlendEquationAlpha = blendEquationAlpha;
631 if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
633 gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) );
635 currentBlendSrc = blendSrc;
636 currentBlendDst = blendDst;
637 currentBlendSrcAlpha = blendSrcAlpha;
638 currentBlendDstAlpha = blendDstAlpha;
644 currentBlending = blending;
645 currentPremultipledAlpha = premultipliedAlpha;
649 function setMaterial( material ) {
651 material.side === DoubleSide
652 ? disable( gl.CULL_FACE )
653 : enable( gl.CULL_FACE );
655 setFlipSided( material.side === BackSide );
657 material.transparent === true
658 ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha )
659 : setBlending( NoBlending );
661 depthBuffer.setFunc( material.depthFunc );
662 depthBuffer.setTest( material.depthTest );
663 depthBuffer.setMask( material.depthWrite );
664 colorBuffer.setMask( material.colorWrite );
666 setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
672 function setFlipSided( flipSided ) {
674 if ( currentFlipSided !== flipSided ) {
678 gl.frontFace( gl.CW );
682 gl.frontFace( gl.CCW );
686 currentFlipSided = flipSided;
692 function setCullFace( cullFace ) {
694 if ( cullFace !== CullFaceNone ) {
696 enable( gl.CULL_FACE );
698 if ( cullFace !== currentCullFace ) {
700 if ( cullFace === CullFaceBack ) {
702 gl.cullFace( gl.BACK );
704 } else if ( cullFace === CullFaceFront ) {
706 gl.cullFace( gl.FRONT );
710 gl.cullFace( gl.FRONT_AND_BACK );
718 disable( gl.CULL_FACE );
722 currentCullFace = cullFace;
726 function setLineWidth( width ) {
728 if ( width !== currentLineWidth ) {
730 if ( lineWidthAvailable ) gl.lineWidth( width );
732 currentLineWidth = width;
738 function setPolygonOffset( polygonOffset, factor, units ) {
740 if ( polygonOffset ) {
742 enable( gl.POLYGON_OFFSET_FILL );
744 if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {
746 gl.polygonOffset( factor, units );
748 currentPolygonOffsetFactor = factor;
749 currentPolygonOffsetUnits = units;
755 disable( gl.POLYGON_OFFSET_FILL );
761 function setScissorTest( scissorTest ) {
765 enable( gl.SCISSOR_TEST );
769 disable( gl.SCISSOR_TEST );
777 function activeTexture( webglSlot ) {
779 if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
781 if ( currentTextureSlot !== webglSlot ) {
783 gl.activeTexture( webglSlot );
784 currentTextureSlot = webglSlot;
790 function bindTexture( webglType, webglTexture ) {
792 if ( currentTextureSlot === null ) {
798 var boundTexture = currentBoundTextures[ currentTextureSlot ];
800 if ( boundTexture === undefined ) {
802 boundTexture = { type: undefined, texture: undefined };
803 currentBoundTextures[ currentTextureSlot ] = boundTexture;
807 if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
809 gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );
811 boundTexture.type = webglType;
812 boundTexture.texture = webglTexture;
818 function compressedTexImage2D() {
822 gl.compressedTexImage2D.apply( gl, arguments );
826 console.error( 'THREE.WebGLState:', error );
832 function texImage2D() {
836 gl.texImage2D.apply( gl, arguments );
840 console.error( 'THREE.WebGLState:', error );
848 function scissor( scissor ) {
850 if ( currentScissor.equals( scissor ) === false ) {
852 gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
853 currentScissor.copy( scissor );
859 function viewport( viewport ) {
861 if ( currentViewport.equals( viewport ) === false ) {
863 gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
864 currentViewport.copy( viewport );
874 for ( var i = 0; i < enabledAttributes.length; i ++ ) {
876 if ( enabledAttributes[ i ] === 1 ) {
878 gl.disableVertexAttribArray( i );
879 enabledAttributes[ i ] = 0;
887 compressedTextureFormats = null;
889 currentTextureSlot = null;
890 currentBoundTextures = {};
892 currentProgram = null;
894 currentBlending = null;
896 currentFlipSided = null;
897 currentCullFace = null;
901 stencilBuffer.reset();
910 stencil: stencilBuffer
913 initAttributes: initAttributes,
914 enableAttribute: enableAttribute,
915 enableAttributeAndDivisor: enableAttributeAndDivisor,
916 disableUnusedAttributes: disableUnusedAttributes,
919 getCompressedTextureFormats: getCompressedTextureFormats,
921 useProgram: useProgram,
923 setBlending: setBlending,
924 setMaterial: setMaterial,
926 setFlipSided: setFlipSided,
927 setCullFace: setCullFace,
929 setLineWidth: setLineWidth,
930 setPolygonOffset: setPolygonOffset,
932 setScissorTest: setScissorTest,
934 activeTexture: activeTexture,
935 bindTexture: bindTexture,
936 compressedTexImage2D: compressedTexImage2D,
937 texImage2D: texImage2D,
949 export { WebGLState };