import { arraySet } from "../utils/helpers";
import { Utils } from "../utils/Utils";
import { LightProps } from "../data/props/LightProps";
import { Matrix3Utilities } from "../geom/Matrix3Utilities";
import { BaseDrawable } from "./BaseDrawable";
import { Container } from "./Container";
const TEMP_ARRAY = [];
/**
* <pre>
* Light
* - Register every Light instance to a LightRenderer
* with LightRenderer.registerLight
* and unregister with LightRenderer.unregisterLight
* </pre>
* @extends {BaseDrawable}
* @property {number} type - Type of the Light
* @property {number} maxShadowStep - The maximum step of shadow caster per pixel
* - Default value is 128
* @property {number} reflectionSize - Reflection size
* @property {number} precision - Shadow precision
* - Default value is 1
* @property {number} angle - Rotation of the light
* @property {number} spotAngle - Angle of the light source
* - Default value is 180deg [hemisphere]
* - 360deg [sphere]
*/
export class Light extends BaseDrawable {
/**
* Creates an instance of Light.
* @constructor
*/
constructor() {
super();
this.props = new LightProps();
this.unregisterData();
this.castShadow = this.shading = true;
this.flattenShadow = false;
this.centerReflection = true;
this.angle = 0;
this.spotAngle = 180 * Utils.THETA;
this.type = Light.Type.POINT;
this.precision = this.diffuse = this.reflectionSize = 1;
this.maxShadowStep = 128;
}
/**
* @ignore
*/
registerData(id, lightData, extensionData) {
this._id = id;
this._matId = id * 16;
this._quadId = this._matId + 4;
this._octId = this._matId + 8;
this._datId = this._matId + 12;
this._lightData = lightData;
this._extensionData = extensionData;
this.$currentColorUpdateId = -1;
this._updateLightProps();
}
/**
* @ignore
*/
unregisterData() {
this.registerData(0, TEMP_ARRAY, TEMP_ARRAY);
}
/**
* Set/Get shadow casting
* @type {boolean}
*/
get castShadow() {
return this._castShadow;
}
set castShadow(v) {
this._castShadow = v;
this._updateLightProps();
}
/**
* Set/Get shading
* @type {boolean}
*/
get shading() {
return this._shading;
}
set shading(v) {
this._shading = v;
this._updateLightProps();
}
/**
* <pre>
* Set/Get flatten the shadow
* - Cast shadow if true and a pixel is higher or equal than the light z position
* - Cast shadow if false and a pixel is higher than the light z position
* </pre>
* @type {boolean}
*/
get flattenShadow() {
return this._flattenShadow;
}
set flattenShadow(v) {
this._flattenShadow = v;
this._updateLightProps();
}
/**
* <pre>
* Set/Get the surface reflects the color of the light
* - If it is false the surface reflects white color
* </pre>
* @type {boolean}
*/
get colorProofReflection() {
return this._colorProofReflection;
}
set colorProofReflection(v) {
this._colorProofReflection = v;
this._updateLightProps();
}
/**
* Set/Get is the reflection moves towards the center of the screen
* @type {boolean}
*/
get centerReflection() {
return this._centerReflection;
}
set centerReflection(v) {
this._centerReflection = v;
this._updateLightProps();
}
/**
* Returns true if the light is renderable
* @returns {boolean}
*/
isOn() {
return this.renderable && this.stage !== null;
}
/**
* Update Light properties
*/
update() {
const lightData = this._lightData;
const datId = this._datId;
if (this.isOn()) {
this.$updateProps();
this._updateColor();
lightData[datId] = lightData[datId - 1] > 0 ? 1 : 0;
lightData[datId + 1] = this.diffuse;
lightData[datId + 2] = this.props.alpha;
lightData[this._matId + 6] = this.props.width;
this._extensionData[this._matId + 2] = this.props.z;
this._extensionData[this._matId] = this.type;
this._extensionData[this._quadId] = this.maxShadowStep;
this._extensionData[this._quadId + 1] = this.reflectionSize;
this._extensionData[this._matId + 3] = this.precision;
this._lightData[this._datId + 3] = this.angle;
this._lightData[this._matId + 7] = this.spotAngle;
} else lightData[datId] = 0;
}
/**
* @ignore
*/
_updateLightProps() {
this._extensionData[this._matId + 1] =
(this._castShadow * 1) |
(this._shading * 2) |
(this._flattenShadow * 4) |
(this._colorProofReflection * 8) |
(this._centerReflection * 16);
}
/**
* @ignore
*/
$updateAdditionalData() {
if (
this.isOn() &&
this.$currentAdditionalPropsUpdateId < this.propsUpdateId
) {
this.$currentAdditionalPropsUpdateId = this.propsUpdateId;
this.$calcBounds();
}
}
/**
* @ignore
*/
$calcCorners() {
Matrix3Utilities.calcCorners(
this.matrixCache,
this.$corners,
this.stage.renderer
);
const corners = this.$corners;
const a = corners[0];
const b = corners[1];
const c = corners[2];
const d = corners[3];
a.x += a.x - d.x + (a.x - c.x);
a.y += a.y - d.y + (a.y - c.y);
c.x += c.x - b.x;
c.y += c.y - b.y;
d.x += d.x - b.x;
d.y += d.y - b.y;
}
/**
* @param {LightProps} props
* @param {Container} parent
* @ignore
*/
$updateTransform(props, parent) {
super.$updateTransform(props, parent);
arraySet(this._lightData, this.matrixCache, this._matId);
}
/**
* @ignore
*/
_updateColor() {
const color = this.color;
if (this.$currentColorUpdateId < color.updateId) {
this.$currentColorUpdateId = color.updateId;
const lightData = this._lightData;
const parentColorCache = this.$parent.colorCache;
const colId = this._octId;
lightData[colId] = parentColorCache[0] * color.r;
lightData[colId + 1] = parentColorCache[1] * color.g;
lightData[colId + 2] = parentColorCache[2] * color.b;
lightData[colId + 3] = parentColorCache[3] * color.a;
}
}
}
/**
* Light type
* @member
* @property {number} POINT Like a lamp light source
* @property {number} DIRECTIONAL Like sunlight
*/
Light.Type = {
POINT: 0,
DIRECTIONAL: 1,
};