text: /**
   * @author alteredq / http://alteredqualia.com/
   * @author mrdoob / http://mrdoob.com/
   */
  
  import { FrontSide, BackSide, DoubleSide, RGBAFormat, NearestFilter, PCFShadowMap, RGBADepthPacking } from '../../constants';
  import { WebGLRenderTarget } from '../WebGLRenderTarget';
  import { ShaderMaterial } from '../../materials/ShaderMaterial';
  import { UniformsUtils } from '../shaders/UniformsUtils';
  import { ShaderLib } from '../shaders/ShaderLib';
  import { MeshDepthMaterial } from '../../materials/MeshDepthMaterial';
  import { Vector4 } from '../../math/Vector4';
  import { Vector3 } from '../../math/Vector3';
  import { Vector2 } from '../../math/Vector2';
  import { Matrix4 } from '../../math/Matrix4';
  import { Frustum } from '../../math/Frustum';
  
  function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) {
  
  	var _gl = _renderer.context,
  	_state = _renderer.state,
  	_frustum = new Frustum(),
  	_projScreenMatrix = new Matrix4(),
  
  	_lightShadows = _lights.shadows,
  
  	_shadowMapSize = new Vector2(),
  	_maxShadowMapSize = new Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ),
  
  	_lookTarget = new Vector3(),
  	_lightPositionWorld = new Vector3(),
  
  	_renderList = [],
  
  	_MorphingFlag = 1,
  	_SkinningFlag = 2,
  
  	_NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,
  
  	_depthMaterials = new Array( _NumberOfMaterialVariants ),
  	_distanceMaterials = new Array( _NumberOfMaterialVariants ),
  
  	_materialCache = {};
  
  	var cubeDirections = [
  		new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
  		new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
  	];
  
  	var cubeUps = [
  		new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
  		new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),	new Vector3( 0, 0, - 1 )
  	];
  
  	var cube2DViewPorts = [
  		new Vector4(), new Vector4(), new Vector4(),
  		new Vector4(), new Vector4(), new Vector4()
  	];
  
  	// init
  
  	var depthMaterialTemplate = new MeshDepthMaterial();
  	depthMaterialTemplate.depthPacking = RGBADepthPacking;
  	depthMaterialTemplate.clipping = true;
  
  	var distanceShader = ShaderLib[ "distanceRGBA" ];
  	var distanceUniforms = UniformsUtils.clone( distanceShader.uniforms );
  
  	for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {
  
  		var useMorphing = ( i & _MorphingFlag ) !== 0;
  		var useSkinning = ( i & _SkinningFlag ) !== 0;
  
  		var depthMaterial = depthMaterialTemplate.clone();
  		depthMaterial.morphTargets = useMorphing;
  		depthMaterial.skinning = useSkinning;
  
  		_depthMaterials[ i ] = depthMaterial;
  
  		var distanceMaterial = new ShaderMaterial( {
  			defines: {
  				'USE_SHADOWMAP': ''
  			},
  			uniforms: distanceUniforms,
  			vertexShader: distanceShader.vertexShader,
  			fragmentShader: distanceShader.fragmentShader,
  			morphTargets: useMorphing,
  			skinning: useSkinning,
  			clipping: true
  		} );
  
  		_distanceMaterials[ i ] = distanceMaterial;
  
  	}
  
  	//
  
  	var scope = this;
  
  	this.enabled = false;
  
  	this.autoUpdate = true;
  	this.needsUpdate = false;
  
  	this.type = PCFShadowMap;
  
  	this.renderReverseSided = true;
  	this.renderSingleSided = true;
  
  	this.render = function ( scene, camera ) {
  
  		if ( scope.enabled === false ) return;
  		if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
  
  		if ( _lightShadows.length === 0 ) return;
  
  		// Set GL state for depth map.
  		_state.clearColor( 1, 1, 1, 1 );
  		_state.disable( _gl.BLEND );
  		_state.setDepthTest( true );
  		_state.setScissorTest( false );
  
  		// render depth map
  
  		var faceCount, isPointLight;
  
  		for ( var i = 0, il = _lightShadows.length; i < il; i ++ ) {
  
  			var light = _lightShadows[ i ];
  			var shadow = light.shadow;
  
  			if ( shadow === undefined ) {
  
  				console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );
  				continue;
  
  			}
  
  			var shadowCamera = shadow.camera;
  
  			_shadowMapSize.copy( shadow.mapSize );
  			_shadowMapSize.min( _maxShadowMapSize );
  
  			if ( (light && light.isPointLight) ) {
  
  				faceCount = 6;
  				isPointLight = true;
  
  				var vpWidth = _shadowMapSize.x;
  				var vpHeight = _shadowMapSize.y;
  
  				// These viewports map a cube-map onto a 2D texture with the
  				// following orientation:
  				//
  				//  xzXZ
  				//   y Y
  				//
  				// X - Positive x direction
  				// x - Negative x direction
  				// Y - Positive y direction
  				// y - Negative y direction
  				// Z - Positive z direction
  				// z - Negative z direction
  
  				// positive X
  				cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
  				// negative X
  				cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
  				// positive Z
  				cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
  				// negative Z
  				cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
  				// positive Y
  				cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
  				// negative Y
  				cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
  
  				_shadowMapSize.x *= 4.0;
  				_shadowMapSize.y *= 2.0;
  
  			} else {
  
  				faceCount = 1;
  				isPointLight = false;
  
  			}
  
  			if ( shadow.map === null ) {
  
  				var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
  
  				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
  
  				shadowCamera.updateProjectionMatrix();
  
  			}
  
  			if ( (shadow && shadow.isSpotLightShadow) ) {
  
  				shadow.update( light );
  
  			}
  
  			var shadowMap = shadow.map;
  			var shadowMatrix = shadow.matrix;
  
  			_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
  			shadowCamera.position.copy( _lightPositionWorld );
  
  			_renderer.setRenderTarget( shadowMap );
  			_renderer.clear();
  
  			// render shadow map for each cube face (if omni-directional) or
  			// run a single pass if not
  
  			for ( var face = 0; face < faceCount; face ++ ) {
  
  				if ( isPointLight ) {
  
  					_lookTarget.copy( shadowCamera.position );
  					_lookTarget.add( cubeDirections[ face ] );
  					shadowCamera.up.copy( cubeUps[ face ] );
  					shadowCamera.lookAt( _lookTarget );
  
  					var vpDimensions = cube2DViewPorts[ face ];
  					_state.viewport( vpDimensions );
  
  				} else {
  
  					_lookTarget.setFromMatrixPosition( light.target.matrixWorld );
  					shadowCamera.lookAt( _lookTarget );
  
  				}
  
  				shadowCamera.updateMatrixWorld();
  				shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
  
  				// compute shadow matrix
  
  				shadowMatrix.set(
  					0.5, 0.0, 0.0, 0.5,
  					0.0, 0.5, 0.0, 0.5,
  					0.0, 0.0, 0.5, 0.5,
  					0.0, 0.0, 0.0, 1.0
  				);
  
  				shadowMatrix.multiply( shadowCamera.projectionMatrix );
  				shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
  
  				// update camera matrices and frustum
  
  				_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
  				_frustum.setFromMatrix( _projScreenMatrix );
  
  				// set object matrices & frustum culling
  
  				_renderList.length = 0;
  
  				projectObject( scene, camera, shadowCamera );
  
  				// render shadow map
  				// render regular objects
  
  				for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
  
  					var object = _renderList[ j ];
  					var geometry = _objects.update( object );
  					var material = object.material;
  
  					if ( (material && material.isMultiMaterial) ) {
  
  						var groups = geometry.groups;
  						var materials = material.materials;
  
  						for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
  
  							var group = groups[ k ];
  							var groupMaterial = materials[ group.materialIndex ];
  
  							if ( groupMaterial.visible === true ) {
  
  								var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
  								_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );
  
  							}
  
  						}
  
  					} else {
  
  						var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
  						_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );
  
  					}
  
  				}
  
  			}
  
  		}
  
  		// Restore GL state.
  		var clearColor = _renderer.getClearColor(),
  		clearAlpha = _renderer.getClearAlpha();
  		_renderer.setClearColor( clearColor, clearAlpha );
  
  		scope.needsUpdate = false;
  
  	};
  
  	function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {
  
  		var geometry = object.geometry;
  
  		var result = null;
  
  		var materialVariants = _depthMaterials;
  		var customMaterial = object.customDepthMaterial;
  
  		if ( isPointLight ) {
  
  			materialVariants = _distanceMaterials;
  			customMaterial = object.customDistanceMaterial;
  
  		}
  
  		if ( ! customMaterial ) {
  
  			var useMorphing = false;
  
  			if ( material.morphTargets ) {
  
  				if ( (geometry && geometry.isBufferGeometry) ) {
  
  					useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;
  
  				} else if ( (geometry && geometry.isGeometry) ) {
  
  					useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0;
  
  				}
  
  			}
  
  			var useSkinning = object.isSkinnedMesh && material.skinning;
  
  			var variantIndex = 0;
  
  			if ( useMorphing ) variantIndex |= _MorphingFlag;
  			if ( useSkinning ) variantIndex |= _SkinningFlag;
  
  			result = materialVariants[ variantIndex ];
  
  		} else {
  
  			result = customMaterial;
  
  		}
  
  		if ( _renderer.localClippingEnabled &&
  			 material.clipShadows === true &&
  				material.clippingPlanes.length !== 0 ) {
  
  			// in this case we need a unique material instance reflecting the
  			// appropriate state
  
  			var keyA = result.uuid, keyB = material.uuid;
  
  			var materialsForVariant = _materialCache[ keyA ];
  
  			if ( materialsForVariant === undefined ) {
  
  				materialsForVariant = {};
  				_materialCache[ keyA ] = materialsForVariant;
  
  			}
  
  			var cachedMaterial = materialsForVariant[ keyB ];
  
  			if ( cachedMaterial === undefined ) {
  
  				cachedMaterial = result.clone();
  				materialsForVariant[ keyB ] = cachedMaterial;
  
  			}
  
  			result = cachedMaterial;
  
  		}
  
  		result.visible = material.visible;
  		result.wireframe = material.wireframe;
  
  		var side = material.side;
  
  		if ( scope.renderSingleSided && side == DoubleSide ) {
  
  			side = FrontSide;
  
  		}
  
  		if ( scope.renderReverseSided ) {
  
  			if ( side === FrontSide ) side = BackSide;
  			else if ( side === BackSide ) side = FrontSide;
  
  		}
  
  		result.side = side;
  
  		result.clipShadows = material.clipShadows;
  		result.clippingPlanes = material.clippingPlanes;
  
  		result.wireframeLinewidth = material.wireframeLinewidth;
  		result.linewidth = material.linewidth;
  
  		if ( isPointLight && result.uniforms.lightPos !== undefined ) {
  
  			result.uniforms.lightPos.value.copy( lightPositionWorld );
  
  		}
  
  		return result;
  
  	}
  
  	function projectObject( object, camera, shadowCamera ) {
  
  		if ( object.visible === false ) return;
  
  		var visible = ( object.layers.mask & camera.layers.mask ) !== 0;
  
  		if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {
  
  			if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
  
  				var material = object.material;
  
  				if ( material.visible === true ) {
  
  					object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
  					_renderList.push( object );
  
  				}
  
  			}
  
  		}
  
  		var children = object.children;
  
  		for ( var i = 0, l = children.length; i < l; i ++ ) {
  
  			projectObject( children[ i ], camera, shadowCamera );
  
  		}
  
  	}
  
  }
  
  
  export { WebGLShadowMap };
  