Source: render/prePass/ShadowMapPass.js

import { DepthMaterial } from '../../material/DepthMaterial.js';
import { DistanceMaterial } from '../../material/DistanceMaterial.js';
import { LIGHT_TYPE, DRAW_SIDE } from '../../const.js';
import { Vector4 } from '../../math/Vector4.js';

var shadowSide = { "front": DRAW_SIDE.BACK, "back": DRAW_SIDE.FRONT, "double": DRAW_SIDE.DOUBLE };

var defaultDepthMaterial = new DepthMaterial();
defaultDepthMaterial.packToRGBA = true;

var defaultDistanceMaterial = new DistanceMaterial();
defaultDistanceMaterial.uniforms = {};

function _getDepthMaterial(renderable, light) {
	defaultDepthMaterial.side = shadowSide[renderable.material.side];
	return defaultDepthMaterial;
}

function _getDistanceMaterial(renderable, light) {
	defaultDistanceMaterial.side = shadowSide[renderable.material.side];
	defaultDistanceMaterial.uniforms["nearDistance"] = light.shadow.cameraNear;
	defaultDistanceMaterial.uniforms["farDistance"] = light.shadow.cameraFar;
	return defaultDistanceMaterial;
}

var oldClearColor = new Vector4();

/**
 * Shadow map pre pass.
 * @constructor
 * @memberof zen3d
 */
function ShadowMapPass() {
	/**
	 * Get depth material function.
	 * Override this to use custom depth material.
	 * @type {Function}
	 */
	this.getDepthMaterial = _getDepthMaterial;

	/**
	 * Get distance material function.
	 * Override this to use custom distance material.
	 * @type {Function}
	 */
	this.getDistanceMaterial = _getDistanceMaterial;
}

/**
 * Render shadow map.
 * @param {zen3d.WebGLCore} glCore
 * @param {zen3d.Scene} scene
 */
ShadowMapPass.prototype.render = function(glCore, scene) {
	var gl = glCore.gl;
	var state = glCore.state;

	var getDepthMaterial = this.getDepthMaterial;
	var getDistanceMaterial = this.getDistanceMaterial;

	// force disable stencil
	var useStencil = state.states[gl.STENCIL_TEST];
	if (useStencil) {
		state.stencilBuffer.setTest(false);
	}

	oldClearColor.copy(state.colorBuffer.getClear());
	state.colorBuffer.setClear(1, 1, 1, 1);

	var lights = scene.lights.shadows;
	for (var i = 0; i < lights.length; i++) {
		var light = lights[i];

		var shadow = light.shadow;

		if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue;

		var camera = shadow.camera;
		var shadowTarget = shadow.renderTarget;
		var isPointLight = light.lightType == LIGHT_TYPE.POINT ? true : false;
		var faces = isPointLight ? 6 : 1;

		if (glCore.capabilities.version >= 2) {
			if (!isPointLight && !shadow.depthMap && !glCore.disableShadowSampler) {
				shadow._initDepthMap();
			}
		}

		for (var j = 0; j < faces; j++) {
			if (isPointLight) {
				shadow.update(light, j);
				shadowTarget.activeCubeFace = j;
			} else {
				shadow.update(light);
			}

			var renderList = scene.updateRenderList(camera);

			glCore.renderTarget.setRenderTarget(shadowTarget);

			glCore.clear(true, true);

			glCore.renderPass(renderList.opaque, camera, {
				getMaterial: function(renderable) {
					return isPointLight ? getDistanceMaterial(renderable, light) : getDepthMaterial(renderable, light);
				},
				ifRender: function(renderable) {
					return renderable.object.castShadow;
				}
			});

			// ignore transparent objects?
		}

		// set generateMipmaps false
		// glCore.renderTarget.updateRenderTargetMipmap(shadowTarget);

		shadow.needsUpdate = false;
	}

	if (useStencil) {
		state.stencilBuffer.setTest(true);
	}

	state.colorBuffer.setClear(oldClearColor.x, oldClearColor.y, oldClearColor.z, oldClearColor.w);
}

export { ShadowMapPass };