Source: render/LightCache.js

import { LIGHT_TYPE } from '../const.js';
import { Vector3 } from '../math/Vector3.js';

var helpVector3 = new Vector3();

var lightCaches = new WeakMap();

function getLightCache(light) {
	if (lightCaches.has(light)) {
		return lightCaches.get(light);
	}

	var cache;
	switch (light.lightType) {
	case LIGHT_TYPE.DIRECT:
		cache = {
			direction: new Float32Array(3),
			color: new Float32Array([0, 0, 0, 1]),
			shadow: 0,
			shadowBias: 0,
			shadowRadius: 1,
			shadowMapSize: new Float32Array(2)
		};
		break;
	case LIGHT_TYPE.POINT:
		cache = {
			position: new Float32Array(3),
			color: new Float32Array([0, 0, 0, 1]),
			distance: 0,
			decay: 0,
			shadow: 0,
			shadowBias: 0,
			shadowRadius: 1,
			shadowMapSize: new Float32Array(2),
			shadowCameraNear: 1,
			shadowCameraFar: 1000
		};
		break;
	case LIGHT_TYPE.SPOT:
		cache = {
			position: new Float32Array(3),
			direction: new Float32Array(3),
			color: new Float32Array([0, 0, 0, 1]),
			distance: 0,
			coneCos: 0,
			penumbraCos: 0,
			decay: 0,
			shadow: 0,
			shadowBias: 0,
			shadowRadius: 1,
			shadowMapSize: new Float32Array(2)
		};
		break;
	}

	lightCaches.set(light, cache);

	return cache;
}

function LightHash() {
	this._factor = new Uint16Array(4);
}

Object.assign(LightHash.prototype, {
	update: function(lights) {
		this._factor[0] = lights.ambientsNum;
		this._factor[1] = lights.directsNum;
		this._factor[2] = lights.pointsNum;
		this._factor[3] = lights.spotsNum;
	},
	compare: function(factor) {
		if (!factor) {
			return false;
		}
		return !(this._factor[0] !== factor[0] ||
			this._factor[1] !== factor[1] ||
			this._factor[2] !== factor[2] ||
			this._factor[3] !== factor[3]);
	},
	copyTo: function(factor) {
		if (!factor) {
			factor = new Uint16Array(4);
		}
		factor.set(this._factor);
		return factor;
	}
});

/**
 * Light cache collect all lights in the scene.
 * @constructor
 * @hideconstructor
 * @memberof zen3d
 */
function LightCache() {
	this.ambient = new Float32Array([0, 0, 0]);
	this.directional = [];
	this.directionalShadowMap = [];
	this.directionalShadowDepthMap = [];
	this.directionalShadowMatrix = [];
	this.point = [];
	this.pointShadowMap = [];
	this.pointShadowMatrix = [];
	this.spot = [];
	this.spotShadowMap = [];
	this.spotShadowDepthMap = [];
	this.spotShadowMatrix = [];
	this.shadows = [];
	this.ambientsNum = 0;
	this.directsNum = 0;
	this.pointsNum = 0;
	this.spotsNum = 0;
	this.directShadowNum = 0;
	this.pointShadowNum = 0;
	this.spotShadowNum = 0;
	this.shadowsNum = 0;
	this.totalNum = 0;

	this.hash = new LightHash();
}

Object.assign(LightCache.prototype, {

	/**
     * Mark count start.
     * @memberof zen3d.LightCache#
     */
	startCount: function () {
		for (var i = 0; i < 3; i++) {
			this.ambient[i] = 0;
		}
		this.shadows.length = 0;
		this.ambientsNum = 0;
		this.directsNum = 0;
		this.pointsNum = 0;
		this.spotsNum = 0;
		this.directShadowNum = 0;
		this.pointShadowNum = 0;
		this.spotShadowNum = 0;
		this.shadowsNum = 0;
		this.totalNum = 0;
	},

	/**
     * Collect a light.
     * @memberof zen3d.LightCache#
     * @param {zen3d.Light} object - The light to be collected.
     */
	add: function (object) {
		if (object.lightType == LIGHT_TYPE.AMBIENT) {
			this._doAddAmbientLight(object);
		} else if (object.lightType == LIGHT_TYPE.DIRECT) {
			this._doAddDirectLight(object);
		} else if (object.lightType == LIGHT_TYPE.POINT) {
			this._doAddPointLight(object);
		} else if (object.lightType == LIGHT_TYPE.SPOT) {
			this._doAddSpotLight(object);
		}

		if (object.castShadow && object.lightType !== LIGHT_TYPE.AMBIENT) {
			this.shadows.push(object);
			this.shadowsNum++;
		}

		this.totalNum++;
	},

	/**
     * Mark count finished.
     * @memberof zen3d.LightCache#
     */
	endCount: function () {
		this.hash.update(this);
	},

	_doAddAmbientLight: function (object) {
		var intensity = object.intensity;
		var color = object.color;

		this.ambient[0] += color.r * intensity;
		this.ambient[1] += color.g * intensity;
		this.ambient[2] += color.b * intensity;

		this.ambientsNum++;
	},

	_doAddDirectLight: function (object) {
		var intensity = object.intensity;
		var color = object.color;

		var cache = getLightCache(object);

		cache.color[0] = color.r * intensity;
		cache.color[1] = color.g * intensity;
		cache.color[2] = color.b * intensity;

		var direction = helpVector3;
		object.getWorldDirection(direction);
		// direction.transformDirection(camera.viewMatrix);

		cache.direction[0] = direction.x;
		cache.direction[1] = direction.y;
		cache.direction[2] = direction.z;

		if (object.castShadow) {
			cache.shadow = 1;
			cache.shadowBias = object.shadow.bias;
			cache.shadowRadius = object.shadow.radius;
			cache.shadowMapSize[0] = object.shadow.mapSize.x;
			cache.shadowMapSize[1] = object.shadow.mapSize.y;

			this.directShadowNum++;
		} else {
			cache.shadow = 0;
		}

		if (object.castShadow) {
			// resize typed array
			var needSize = (this.directsNum + 1) * 16;
			if (this.directionalShadowMatrix.length < needSize) {
				var old = this.directionalShadowMatrix;
				this.directionalShadowMatrix = new Float32Array(needSize);
				this.directionalShadowMatrix.set(old);
			}

			this.directionalShadowMatrix.set(object.shadow.matrix.elements, this.directsNum * 16);
			this.directionalShadowMap[this.directsNum] = object.shadow.map;
			this.directionalShadowDepthMap[this.directsNum] = object.shadow.depthMap;
		}

		this.directional[this.directsNum] = cache;

		this.directsNum++;
	},

	_doAddPointLight: function (object) {
		var intensity = object.intensity;
		var color = object.color;
		var distance = object.distance;
		var decay = object.decay;

		var cache = getLightCache(object);

		cache.color[0] = color.r * intensity;
		cache.color[1] = color.g * intensity;
		cache.color[2] = color.b * intensity;

		cache.distance = distance;
		cache.decay = decay;

		var position = helpVector3.setFromMatrixPosition(object.worldMatrix);// .applyMatrix4(camera.viewMatrix);

		cache.position[0] = position.x;
		cache.position[1] = position.y;
		cache.position[2] = position.z;

		if (object.castShadow) {
			cache.shadow = 1;
			cache.shadowBias = object.shadow.bias;
			cache.shadowRadius = object.shadow.radius;
			cache.shadowMapSize[0] = object.shadow.mapSize.x;
			cache.shadowMapSize[1] = object.shadow.mapSize.y;
			cache.shadowCameraNear = object.shadow.cameraNear;
			cache.shadowCameraFar = object.shadow.cameraFar;

			this.pointShadowNum++;
		} else {
			cache.shadow = 0;
		}

		if (object.castShadow) {
			// resize typed array
			var needSize = (this.pointsNum + 1) * 16;
			if (this.pointShadowMatrix.length < needSize) {
				var old = this.pointShadowMatrix;
				this.pointShadowMatrix = new Float32Array(needSize);
				this.pointShadowMatrix.set(old);
			}

			this.pointShadowMatrix.set(object.shadow.matrix.elements, this.pointsNum * 16);
			this.pointShadowMap[this.pointsNum] = object.shadow.map;
		}

		this.point[this.pointsNum] = cache;

		this.pointsNum++;
	},

	_doAddSpotLight: function (object) {
		var intensity = object.intensity;
		var color = object.color;
		var distance = object.distance;
		var decay = object.decay;

		var cache = getLightCache(object);

		cache.color[0] = color.r * intensity;
		cache.color[1] = color.g * intensity;
		cache.color[2] = color.b * intensity;

		cache.distance = distance;
		cache.decay = decay;

		var position = helpVector3.setFromMatrixPosition(object.worldMatrix);// .applyMatrix4(camera.viewMatrix);

		cache.position[0] = position.x;
		cache.position[1] = position.y;
		cache.position[2] = position.z;

		var direction = helpVector3;
		object.getWorldDirection(helpVector3);
		// helpVector3.transformDirection(camera.viewMatrix);

		cache.direction[0] = direction.x;
		cache.direction[1] = direction.y;
		cache.direction[2] = direction.z;

		var coneCos = Math.cos(object.angle);
		var penumbraCos = Math.cos(object.angle * (1 - object.penumbra));

		cache.coneCos = coneCos;
		cache.penumbraCos = penumbraCos;

		if (object.castShadow) {
			cache.shadow = 1;
			cache.shadowBias = object.shadow.bias;
			cache.shadowRadius = object.shadow.radius;
			cache.shadowMapSize[0] = object.shadow.mapSize.x;
			cache.shadowMapSize[1] = object.shadow.mapSize.y;

			this.spotShadowNum++;
		} else {
			cache.shadow = 0;
		}

		if (object.castShadow) {
			// resize typed array
			var needSize = (this.spotsNum + 1) * 16;
			if (this.spotShadowMatrix.length < needSize) {
				var old = this.spotShadowMatrix;
				this.spotShadowMatrix = new Float32Array(needSize);
				this.spotShadowMatrix.set(old);
			}

			this.spotShadowMatrix.set(object.shadow.matrix.elements, this.spotsNum * 16);
			this.spotShadowMap[this.spotsNum] = object.shadow.map;
			this.spotShadowDepthMap[this.spotsNum] = object.shadow.depthMap;
		}

		this.spot[this.spotsNum] = cache;

		this.spotsNum++;
	}

});

export { LightCache };