Source: components/CameraEffectComponent.js

import { ObjectProxy } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { BaseComponent } from './BaseComponent';

const __ = {
	private: Symbol('private'),
}

const cKeys = [
	'glow',
	'innerGlow',
	'tailing',
	'radial',
	'ghosting',
	'lineBloom',
];

/**
 * @class CameraEffectComponent
 * The camera effect compnent.
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class CameraEffectComponent extends BaseComponent {

	/**
	 * The rendering effect by camera, some object post effect(s) can be changed here.
	 */
	constructor() {
		super();

		this[__.private] = {};
		let _private = this[__.private];

		_private.needsRefresh = false;

		/**
		 * Configuration about glow.
		 * @typedef {Object} CameraGlowEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Number} strength The strength.
		 * @property {Number} threshold The threshold.
		 * @property {Number} radius True radius.
		 */

		_private.glow = {
			enable: true,
			strength: 1.5,
			threshold: 0.001,
			radius: 0.4
		};

		/**
		 * Configuration about inner glow.
		 * @typedef {Object} CameraInnerGlowEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Array<Number>} color The color.
		 * @property {Number} strength The strength.
		 * @property {Number} stride True stride.
		 */

		_private.innerGlow = {
			color: [1, 0, 0, 1],
			enable: true,
			strength: 2,
			stride: 10,
		};

		/**
		 * Configuration about tailing.
		 * @typedef {Object} CameraTailingEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Array<Number>} direction The direction.
		 * @property {Array<Number>} center The center.
		 */

		_private.tailing = {
			enable: true,
			direction: [0, 1],
			center: [0.5, 0.5]
		};

		/**
		 * Configuration about radial.
		 * @typedef {Object} CameraRadialEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Array<Number>} center The center.
		 */

		_private.radial = {
			enable: true,
			center: [0.5, 0.5]
		};

		/**
		 * Configuration about ghosting.
		 * @typedef {Object} CameraGhostingEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Array<Number>} center The center.
		 */

		_private.ghosting = {
			enable: true,
			center: [0.5, 0.5]
		};

		/**
		 * Configuration about line bloom.
		 * @typedef {Object} CameraLineBloomEffect
		 * @property {Boolean} enable True indicates enable it.
		 * @property {Number} blendRate The blend rate.
		 * @property {Number} blurSize The blur size.
		 * @property {Number} strength The strength.
		 */

		_private.lineBloom = {
			enable: true,
			blendRate: 0.7,
			blurSize: 1,
			strength: 1,
		};

		_private.onChange = null;

		_private.enable = false;

		// Initialize config
		_private.objectProxy = new ObjectProxy({
			data: {
				glow: _private.glow,
				innerGlow: _private.innerGlow,
				tailing: _private.tailing,
				radial: _private.radial,
				ghosting: _private.ghosting,
				lineBloom: _private.lineBloom,
			},
			onChange: function (ev) {
				_private.needsRefresh = true;

				if (_private.onChange) {
					_private.onChange(ev);
				}
			}
		});

		// Show config always
		Object.defineProperty(this, 'customFormatters', {
			enumerable: false,
			configurable: false,
			get: function () {
				return ['object', { object: _private.objectProxy.dataProxy }];
			}
		});
	}

	// #region Private

	_refreshEffect() {
		let _private = this[__.private];

		let cameraNode = this.object.node;
		cameraNode.setEffectConfig('glow', _private.glow);
		cameraNode.setEffectConfig('innerGlow', _private.innerGlow);
		cameraNode.setEffectConfig('tailing', _private.tailing);
		cameraNode.setEffectConfig('radial', _private.radial);
		cameraNode.setEffectConfig('ghosting', _private.ghosting);
		cameraNode.setEffectConfig('lineBloom', _private.lineBloom);

		// Check whether disable it
		if (!_private.enable) {
			cKeys.forEach(key => {
				cameraNode.setEffectConfig(key, { enable: false });
			});
		}
	}

	_refresh() {
		let _private = this[__.private];

		if (!_private.needsRefresh) {
			return;
		}

		_private.needsRefresh = false;

		this._refreshEffect();
	}

	// #endregion

	// #region BaseComponent Overrides

	onAdd(object) {
		super.onAdd(object);

		// Refresh config before render if needed
		this.app.addBeforeRenderCallback(this._onBeforeRenderCallback = () => {
			this._refresh();
		});

		// Refresh effect by current config
		this._refreshEffect();
	}

	onRemove() {
		let _private = this[__.private];

		_private.objectProxy.dispose();

		this.app.removeBeforeRenderCallback(this._onBeforeRenderCallback);

		super.onRemove();
	}

	// #endregion

	/**
	 * Enable/Disable.
	 * @type {Boolean}
	 * @public
	 */
	get enable() {
		let _private = this[__.private];

		return _private.enable;
	}
	set enable(value) {
		let _private = this[__.private];

		if (_private.enable == value) {
			return;
		}

		_private.enable = value;

		this._refreshEffect();
	}

	/**
	 * Get the glow.
	 * @type {CameraGlowEffect}
	 * @public
	 */
	get glow() {
		return this[__.private].objectProxy.dataProxy.glow;
	}

	/**
	 * Get the inner glow.
	 * @type {CameraInnerGlowEffect}
	 * @public
	 */
	get innerGlow() {
		return this[__.private].objectProxy.dataProxy.innerGlow;
	}

	/**
	 * Get the tailing.
	 * @type {CameraTailingEffect}
	 * @public
	 */
	get tailing() {
		return this[__.private].objectProxy.dataProxy.tailing;
	}

	/**
	 * Get the radial.
	 * @type {CameraRadialEffect}
	 * @public
	 */
	get radial() {
		return this[__.private].objectProxy.dataProxy.radial;
	}

	/**
	 * Get the ghosting.
	 * @type {CameraGhostingEffect}
	 * @public
	 */
	get ghosting() {
		return this[__.private].objectProxy.dataProxy.ghosting;
	}

	/**
	 * Get the lineBloom.
	 * @type {CameraLineBloomEffect}
	 * @public
	 */
	get lineBloom() {
		return this[__.private].objectProxy.dataProxy.lineBloom;
	}

	/**
	 * Get/Set config.
	 * @type {Object}
	 * @public
	 */
	get config() {
		return this[__.private].objectProxy.dataProxy;
	}
	set config(value = {}) {
		let _private = this[__.private];

		let to = _private.objectProxy.dataProxy;

		Utils.mergeObject(to.glow, value.glow, true);
		Utils.mergeObject(to.innerGlow, value.innerGlow, true);
		Utils.mergeObject(to.tailing, value.tailing, true);
		Utils.mergeObject(to.radial, value.radial, true);
		Utils.mergeObject(to.ghosting, value.ghosting, true);
		Utils.mergeObject(to.lineBloom, value.lineBloom, true);
	}

	/**
	 * Get/Set when change callback function.
	 * @type {Function}
	 * @private
	 */
	get onChange() {
		let _private = this[__.private];

		return _private.onChange;
	}
	set onChange(value) {
		let _private = this[__.private];

		_private.onChange = value;
	}

}

export { CameraEffectComponent }