File: //var/www/aspa/three/renderers/webgl/WebGLLights.js
import { Color } from '../../math/Color.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { UniformsLib } from '../shaders/UniformsLib.js';
function UniformsCache() {
	const lights = {};
	return {
		get: function ( light ) {
			if ( lights[ light.id ] !== undefined ) {
				return lights[ light.id ];
			}
			let uniforms;
			switch ( light.type ) {
				case 'DirectionalLight':
					uniforms = {
						direction: new Vector3(),
						color: new Color()
					};
					break;
				case 'SpotLight':
					uniforms = {
						position: new Vector3(),
						direction: new Vector3(),
						color: new Color(),
						distance: 0,
						coneCos: 0,
						penumbraCos: 0,
						decay: 0
					};
					break;
				case 'PointLight':
					uniforms = {
						position: new Vector3(),
						color: new Color(),
						distance: 0,
						decay: 0
					};
					break;
				case 'HemisphereLight':
					uniforms = {
						direction: new Vector3(),
						skyColor: new Color(),
						groundColor: new Color()
					};
					break;
				case 'RectAreaLight':
					uniforms = {
						color: new Color(),
						position: new Vector3(),
						halfWidth: new Vector3(),
						halfHeight: new Vector3()
					};
					break;
			}
			lights[ light.id ] = uniforms;
			return uniforms;
		}
	};
}
function ShadowUniformsCache() {
	const lights = {};
	return {
		get: function ( light ) {
			if ( lights[ light.id ] !== undefined ) {
				return lights[ light.id ];
			}
			let uniforms;
			switch ( light.type ) {
				case 'DirectionalLight':
					uniforms = {
						shadowBias: 0,
						shadowNormalBias: 0,
						shadowRadius: 1,
						shadowMapSize: new Vector2()
					};
					break;
				case 'SpotLight':
					uniforms = {
						shadowBias: 0,
						shadowNormalBias: 0,
						shadowRadius: 1,
						shadowMapSize: new Vector2()
					};
					break;
				case 'PointLight':
					uniforms = {
						shadowBias: 0,
						shadowNormalBias: 0,
						shadowRadius: 1,
						shadowMapSize: new Vector2(),
						shadowCameraNear: 1,
						shadowCameraFar: 1000
					};
					break;
				// TODO (abelnation): set RectAreaLight shadow uniforms
			}
			lights[ light.id ] = uniforms;
			return uniforms;
		}
	};
}
let nextVersion = 0;
function shadowCastingAndTexturingLightsFirst( lightA, lightB ) {
	return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 );
}
function WebGLLights( extensions, capabilities ) {
	const cache = new UniformsCache();
	const shadowCache = ShadowUniformsCache();
	const state = {
		version: 0,
		hash: {
			directionalLength: - 1,
			pointLength: - 1,
			spotLength: - 1,
			rectAreaLength: - 1,
			hemiLength: - 1,
			numDirectionalShadows: - 1,
			numPointShadows: - 1,
			numSpotShadows: - 1,
			numSpotMaps: - 1,
			numLightProbes: - 1
		},
		ambient: [ 0, 0, 0 ],
		probe: [],
		directional: [],
		directionalShadow: [],
		directionalShadowMap: [],
		directionalShadowMatrix: [],
		spot: [],
		spotLightMap: [],
		spotShadow: [],
		spotShadowMap: [],
		spotLightMatrix: [],
		rectArea: [],
		rectAreaLTC1: null,
		rectAreaLTC2: null,
		point: [],
		pointShadow: [],
		pointShadowMap: [],
		pointShadowMatrix: [],
		hemi: [],
		numSpotLightShadowsWithMaps: 0,
		numLightProbes: 0
	};
	for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() );
	const vector3 = new Vector3();
	const matrix4 = new Matrix4();
	const matrix42 = new Matrix4();
	function setup( lights, useLegacyLights ) {
		let r = 0, g = 0, b = 0;
		for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 );
		let directionalLength = 0;
		let pointLength = 0;
		let spotLength = 0;
		let rectAreaLength = 0;
		let hemiLength = 0;
		let numDirectionalShadows = 0;
		let numPointShadows = 0;
		let numSpotShadows = 0;
		let numSpotMaps = 0;
		let numSpotShadowsWithMaps = 0;
		let numLightProbes = 0;
		// ordering : [shadow casting + map texturing, map texturing, shadow casting, none ]
		lights.sort( shadowCastingAndTexturingLightsFirst );
		// artist-friendly light intensity scaling factor
		const scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1;
		for ( let i = 0, l = lights.length; i < l; i ++ ) {
			const light = lights[ i ];
			const color = light.color;
			const intensity = light.intensity;
			const distance = light.distance;
			const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;
			if ( light.isAmbientLight ) {
				r += color.r * intensity * scaleFactor;
				g += color.g * intensity * scaleFactor;
				b += color.b * intensity * scaleFactor;
			} else if ( light.isLightProbe ) {
				for ( let j = 0; j < 9; j ++ ) {
					state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity );
				}
				numLightProbes ++;
			} else if ( light.isDirectionalLight ) {
				const uniforms = cache.get( light );
				uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );
				if ( light.castShadow ) {
					const shadow = light.shadow;
					const shadowUniforms = shadowCache.get( light );
					shadowUniforms.shadowBias = shadow.bias;
					shadowUniforms.shadowNormalBias = shadow.normalBias;
					shadowUniforms.shadowRadius = shadow.radius;
					shadowUniforms.shadowMapSize = shadow.mapSize;
					state.directionalShadow[ directionalLength ] = shadowUniforms;
					state.directionalShadowMap[ directionalLength ] = shadowMap;
					state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;
					numDirectionalShadows ++;
				}
				state.directional[ directionalLength ] = uniforms;
				directionalLength ++;
			} else if ( light.isSpotLight ) {
				const uniforms = cache.get( light );
				uniforms.position.setFromMatrixPosition( light.matrixWorld );
				uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor );
				uniforms.distance = distance;
				uniforms.coneCos = Math.cos( light.angle );
				uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
				uniforms.decay = light.decay;
				state.spot[ spotLength ] = uniforms;
				const shadow = light.shadow;
				if ( light.map ) {
					state.spotLightMap[ numSpotMaps ] = light.map;
					numSpotMaps ++;
					// make sure the lightMatrix is up to date
					// TODO : do it if required only
					shadow.updateMatrices( light );
					if ( light.castShadow ) numSpotShadowsWithMaps ++;
				}
				state.spotLightMatrix[ spotLength ] = shadow.matrix;
				if ( light.castShadow ) {
					const shadowUniforms = shadowCache.get( light );
					shadowUniforms.shadowBias = shadow.bias;
					shadowUniforms.shadowNormalBias = shadow.normalBias;
					shadowUniforms.shadowRadius = shadow.radius;
					shadowUniforms.shadowMapSize = shadow.mapSize;
					state.spotShadow[ spotLength ] = shadowUniforms;
					state.spotShadowMap[ spotLength ] = shadowMap;
					numSpotShadows ++;
				}
				spotLength ++;
			} else if ( light.isRectAreaLight ) {
				const uniforms = cache.get( light );
				uniforms.color.copy( color ).multiplyScalar( intensity );
				uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
				uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );
				state.rectArea[ rectAreaLength ] = uniforms;
				rectAreaLength ++;
			} else if ( light.isPointLight ) {
				const uniforms = cache.get( light );
				uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );
				uniforms.distance = light.distance;
				uniforms.decay = light.decay;
				if ( light.castShadow ) {
					const shadow = light.shadow;
					const shadowUniforms = shadowCache.get( light );
					shadowUniforms.shadowBias = shadow.bias;
					shadowUniforms.shadowNormalBias = shadow.normalBias;
					shadowUniforms.shadowRadius = shadow.radius;
					shadowUniforms.shadowMapSize = shadow.mapSize;
					shadowUniforms.shadowCameraNear = shadow.camera.near;
					shadowUniforms.shadowCameraFar = shadow.camera.far;
					state.pointShadow[ pointLength ] = shadowUniforms;
					state.pointShadowMap[ pointLength ] = shadowMap;
					state.pointShadowMatrix[ pointLength ] = light.shadow.matrix;
					numPointShadows ++;
				}
				state.point[ pointLength ] = uniforms;
				pointLength ++;
			} else if ( light.isHemisphereLight ) {
				const uniforms = cache.get( light );
				uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor );
				uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor );
				state.hemi[ hemiLength ] = uniforms;
				hemiLength ++;
			}
		}
		if ( rectAreaLength > 0 ) {
			if ( capabilities.isWebGL2 ) {
				// WebGL 2
				if ( extensions.has( 'OES_texture_float_linear' ) === true ) {
					state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;
					state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;
				} else {
					state.rectAreaLTC1 = UniformsLib.LTC_HALF_1;
					state.rectAreaLTC2 = UniformsLib.LTC_HALF_2;
				}
			} else {
				// WebGL 1
				if ( extensions.has( 'OES_texture_float_linear' ) === true ) {
					state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;
					state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;
				} else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) {
					state.rectAreaLTC1 = UniformsLib.LTC_HALF_1;
					state.rectAreaLTC2 = UniformsLib.LTC_HALF_2;
				} else {
					console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' );
				}
			}
		}
		state.ambient[ 0 ] = r;
		state.ambient[ 1 ] = g;
		state.ambient[ 2 ] = b;
		const hash = state.hash;
		if ( hash.directionalLength !== directionalLength ||
			hash.pointLength !== pointLength ||
			hash.spotLength !== spotLength ||
			hash.rectAreaLength !== rectAreaLength ||
			hash.hemiLength !== hemiLength ||
			hash.numDirectionalShadows !== numDirectionalShadows ||
			hash.numPointShadows !== numPointShadows ||
			hash.numSpotShadows !== numSpotShadows ||
			hash.numSpotMaps !== numSpotMaps ||
			hash.numLightProbes !== numLightProbes ) {
			state.directional.length = directionalLength;
			state.spot.length = spotLength;
			state.rectArea.length = rectAreaLength;
			state.point.length = pointLength;
			state.hemi.length = hemiLength;
			state.directionalShadow.length = numDirectionalShadows;
			state.directionalShadowMap.length = numDirectionalShadows;
			state.pointShadow.length = numPointShadows;
			state.pointShadowMap.length = numPointShadows;
			state.spotShadow.length = numSpotShadows;
			state.spotShadowMap.length = numSpotShadows;
			state.directionalShadowMatrix.length = numDirectionalShadows;
			state.pointShadowMatrix.length = numPointShadows;
			state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps;
			state.spotLightMap.length = numSpotMaps;
			state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps;
			state.numLightProbes = numLightProbes;
			hash.directionalLength = directionalLength;
			hash.pointLength = pointLength;
			hash.spotLength = spotLength;
			hash.rectAreaLength = rectAreaLength;
			hash.hemiLength = hemiLength;
			hash.numDirectionalShadows = numDirectionalShadows;
			hash.numPointShadows = numPointShadows;
			hash.numSpotShadows = numSpotShadows;
			hash.numSpotMaps = numSpotMaps;
			hash.numLightProbes = numLightProbes;
			state.version = nextVersion ++;
		}
	}
	function setupView( lights, camera ) {
		let directionalLength = 0;
		let pointLength = 0;
		let spotLength = 0;
		let rectAreaLength = 0;
		let hemiLength = 0;
		const viewMatrix = camera.matrixWorldInverse;
		for ( let i = 0, l = lights.length; i < l; i ++ ) {
			const light = lights[ i ];
			if ( light.isDirectionalLight ) {
				const uniforms = state.directional[ directionalLength ];
				uniforms.direction.setFromMatrixPosition( light.matrixWorld );
				vector3.setFromMatrixPosition( light.target.matrixWorld );
				uniforms.direction.sub( vector3 );
				uniforms.direction.transformDirection( viewMatrix );
				directionalLength ++;
			} else if ( light.isSpotLight ) {
				const uniforms = state.spot[ spotLength ];
				uniforms.position.setFromMatrixPosition( light.matrixWorld );
				uniforms.position.applyMatrix4( viewMatrix );
				uniforms.direction.setFromMatrixPosition( light.matrixWorld );
				vector3.setFromMatrixPosition( light.target.matrixWorld );
				uniforms.direction.sub( vector3 );
				uniforms.direction.transformDirection( viewMatrix );
				spotLength ++;
			} else if ( light.isRectAreaLight ) {
				const uniforms = state.rectArea[ rectAreaLength ];
				uniforms.position.setFromMatrixPosition( light.matrixWorld );
				uniforms.position.applyMatrix4( viewMatrix );
				// extract local rotation of light to derive width/height half vectors
				matrix42.identity();
				matrix4.copy( light.matrixWorld );
				matrix4.premultiply( viewMatrix );
				matrix42.extractRotation( matrix4 );
				uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
				uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );
				uniforms.halfWidth.applyMatrix4( matrix42 );
				uniforms.halfHeight.applyMatrix4( matrix42 );
				rectAreaLength ++;
			} else if ( light.isPointLight ) {
				const uniforms = state.point[ pointLength ];
				uniforms.position.setFromMatrixPosition( light.matrixWorld );
				uniforms.position.applyMatrix4( viewMatrix );
				pointLength ++;
			} else if ( light.isHemisphereLight ) {
				const uniforms = state.hemi[ hemiLength ];
				uniforms.direction.setFromMatrixPosition( light.matrixWorld );
				uniforms.direction.transformDirection( viewMatrix );
				hemiLength ++;
			}
		}
	}
	return {
		setup: setup,
		setupView: setupView,
		state: state
	};
}
export { WebGLLights };