Source: sg2d-effects.js

"use strict";

import SG2DConsts from "./sg2d-consts.js";
import SG2DUtils from "./sg2d-utils.js";

/**
 * Эффект
 * @typedef SG2D.Effects.Effect
 * @type {object}
 * @property {Number} type - Тип фильтра: SG2D.Effects.TYPE_SHADOWS | SG2D.Effects.TYPE_BEVELS | SG2D.Effects.TYPE_DISPLACEMENT
 * @property {string|Number} layer
 * @property {PIXI.Filter} filter
 * @property {object} options - Специфические настройки фильтра
 */

/**
 * Эффекты, в том числе графические шейдеры
 * @alias SG2D.Effects
 */
class SG2DEffects {
	
	constructor() {
		this.effects = new Set();
	}
	
	/** @private */
	_sg2dconnect(sg2d) {	
		this.sg2d = sg2d;
		
		this.sg2d.camera.on("scale", this._onCameraScale.bind(this), void 0, void 0, true);
		this.sg2d.camera.on("rotate", this._onCameraRotate.bind(this), void 0, void 0, true);
	}
	
	_onCameraScale(scale, scale_prev) {
		for (var effect of this.effects) {
			switch (effect.type) {
				case SG2DEffects.TYPE_SHADOWS:
					effect.filter.distance *= scale / scale_prev;
					break;
				case SG2DEffects.TYPE_BEVELS:
					effect.filter.thickness *= scale / scale_prev;
					break;
				case SG2DEffects.TYPE_DISPLACEMENT:
					effect.options.scale /= scale / scale_prev;
					effect.sprite.scale.set(effect.options.scale);
					break;
			}
		}
	}
	
	_onCameraRotate(rotate) {
		for (var effect of this.effects) {
			switch (effect.type) {
				case SG2DEffects.TYPE_SHADOWS: case SG2DEffects.TYPE_BEVELS:
					effect.filter.rotation = effect.options.rotation - rotate;
					break;
			}
		}
	}
	
	/**
	 * Добавляет тени
	 * @param {object} config
	 * @param {string|Number} config.layer
	 * @param {Number} [config.rotation=135]
	 * @param {Number} [config.distance=8]
	 * @param {Number} [config.alpha=0.6]
	 * @return {SG2D.Effects.Effect}
	 */
	addShadowsToLayer(config) {
		var container = this.sg2d.layers[config.layer].container;
		container.filters = container.filters || [];
		var rotation = SG2DUtils.ifUndefined(config.rotation, 135);
		var effect = {
			type: SG2DEffects.TYPE_SHADOWS,
			layer: config.layer,
			filter: new PIXI.filters.DropShadowFilter({
				rotation: rotation - this.sg2d.camera.properties.rotate,
				distance: SG2DUtils.ifUndefined(config.distance, 8),
				alpha: SG2DUtils.ifUndefined(config.alpha, 0.6)
			}),
			options: {
				rotation: rotation
			}
		};
		container.filters.push(effect.filter);
		
		this.effects.add(effect);
		return effect;
	}
	
	/**
	 * Добавляет эффект приподнятости
	 * @param {object} config
	 * @param {string|Number} config.layer
	 * @param {Number} [config.rotation=135]
	 * @param {Number} [config.thickness=2]
	 * @param {Number} [config.lightAlpha=0.8]
	 * @param {Number} [config.shadowAlpha=0.8]
	 * @return {SG2D.Effects.Effect}
	 */
	addBevelsToLayer(config) {
		var container = this.sg2d.layers[config.layer].container;
		container.filters = container.filters || [];
		var rotation = SG2DUtils.ifUndefined(config.rotation, 135);
		var effect = {
			type: SG2DEffects.TYPE_BEVELS,
			layer: config.layer,
			filter: new PIXI.filters.BevelFilter({
				rotation: rotation - this.sg2d.camera.properties.rotate,
				thickness: SG2DUtils.ifUndefined(config.thickness, 2),
				lightAlpha: SG2DUtils.ifUndefined(config.lightAlpha, 0.8),
				shadowAlpha: SG2DUtils.ifUndefined(config.shadowAlpha, 0.8)
			}),
			options: {
				rotation: rotation
			}
		};
		effect.filter.padding = 1;
		container.filters.push(effect.filter);
		
		this.effects.add(effect);
		return effect;
	}
	
	/**
	 * Добавляет эффект искажения
	 * @param {object} config
	 * @param {string|Number} config.layer
	 * @param {string|PIXI.Texture} config.texture
	 * @param {Number} [config.scale=1]
	 * @param {function} [config.iterate] - Колбэк, выполняемый в каждую итерацию (кадр)
	 * @param {Number} [config.animationStep=1] - Параметр доступен в iterate() в this.options
	 * @return {SG2D.Effects.Effect}
	 */
	addDisplacementToLayer(config) {
		var container = this.sg2d.layers[config.layer].container;
		container.filters = container.filters || [];
		
		var effect = {
			type: SG2DEffects.TYPE_DISPLACEMENT,
			layer: config.layer,
			options: {
				texture: config.texture || SG2DConsts.TILE_OVERRIDE,
				scale: config.scale || 1
			}
		};
		
		if (typeof config.iterate === "function") {
			effect.iterate = config.iterate.bind(effect);
			effect.options.animationStep = config.animationStep || 1;
		}
		
		effect.sprite = PIXI.Sprite.from(effect.options.texture);
		effect.sprite.scale.set(effect.options.scale);
		effect.sprite.texture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
		container.addChild(effect.sprite);
		
		effect.filter = new PIXI.filters.DisplacementFilter(effect.sprite);
		container.filters.push(effect.filter);
		
		this.effects.add(effect);
		return effect;
	}
}

SG2DEffects.TYPE_SHADOWS = 1;
SG2DEffects.TYPE_BEVELS = 2;
SG2DEffects.TYPE_DISPLACEMENT = 3;

export default SG2DEffects;