/**
A **DirLight** is a directional light source that illuminates all {{#crossLink "Mesh"}}Meshes{{/crossLink}} equally
from a given direction.
## Overview
* DirLights have a direction, but no position.
* The direction is the **direction that the light is emitted in**.
* DirLights may be defined in either **World** or **View** coordinate space. When in World-space, their direction
is relative to the World coordinate system, and will appear to move as the {{#crossLink "Camera"}}{{/crossLink}} moves.
When in View-space, their direction is relative to the View coordinate system, and will behave as if fixed to the viewer's
head as the {{#crossLink "Camera"}}{{/crossLink}} moves.
* A DirLight can also have a {{#crossLink "Shadow"}}{{/crossLink}} component, to configure it to cast a shadow.
* {{#crossLink "AmbientLight"}}{{/crossLink}}, {{#crossLink "DirLight"}}{{/crossLink}},
{{#crossLink "SpotLight"}}{{/crossLink}} and {{#crossLink "PointLight"}}{{/crossLink}} instances are registered by ID
on {{#crossLink "Scene/lights:property"}}Scene#lights{{/crossLink}} for convenient access.
## Examples
* [View-space directional three-point lighting](../../examples/#lights_directional_view_threePoint)
* [World-space directional three-point lighting](../../examples/#lights_directional_world_threePoint)
## Usage
In the example below we'll customize the default Scene's light sources, defining an AmbientLight and a couple of
DirLights, then create a Phong-shaded box mesh.
````javascript
new xeogl.AmbientLight({
color: [0.8, 0.8, 0.8],
intensity: 0.5
});
new xeogl.DirLight({
dir: [1, 1, 1], // Direction the light is shining in
color: [0.5, 0.7, 0.5],
intensity: 1.0,
space: "view", // Other option is "world", for World-space
shadow: false // Default
});
new xeogl.DirLight({
dir: [0.2, -0.8, 0.8],
color: [0.8, 0.8, 0.8],
intensity: 0.5,
space: "view",
shadow: false
});
// Create box mesh
new xeogl.Mesh({
material: new xeogl.PhongMaterial({
ambient: [0.5, 0.5, 0.5],
diffuse: [1,0.3,0.3]
}),
geometry: new xeogl.BoxGeometry()
});
````
@class DirLight
@module xeogl
@submodule lighting
@constructor
@param [owner] {Component} Owner component. When destroyed, the owner will destroy this component as well. Creates this component within the default {{#crossLink "Scene"}}{{/crossLink}} when omitted.
@param [cfg] {*} The DirLight configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this DirLight.
@param [cfg.dir=[1.0, 1.0, 1.0]] {Float32Array} A unit vector indicating the direction that the light is shining,
given in either World or View space, depending on the value of the **space** parameter.
@param [cfg.color=[0.7, 0.7, 0.8 ]] {Float32Array} The color of this DirLight.
@param [cfg.intensity=1.0 ] {Number} The intensity of this DirLight, as a factor in range ````[0..1]````.
@param [cfg.space="view"] {String} The coordinate system the DirLight is defined in - "view" or "space".
@param [cfg.shadow=false] {Boolean} Flag which indicates if this DirLight casts a shadow.
@extends Component
*/
import {Component} from '../component.js';
import {State} from '../renderer/state.js';
import {RenderBuffer} from '../renderer/renderBuffer.js';
import {math} from '../math/math.js';
import {componentClasses} from "./../componentClasses.js";
const type = "xeogl.DirLight";
class DirLight extends Component {
/**
JavaScript class name for this Component.
For example: "xeogl.AmbientLight", "xeogl.MetallicMaterial" etc.
@property type
@type String
@final
*/
get type() {
return type;
}
init(cfg) {
super.init(cfg);
const self = this;
this._shadowRenderBuf = null;
this._shadowViewMatrix = null;
this._shadowProjMatrix = null;
this._shadowViewMatrixDirty = true;
this._shadowProjMatrixDirty = true;
this._state = new State({
type: "dir",
dir: math.vec3([1.0, 1.0, 1.0]),
color: math.vec3([0.7, 0.7, 0.8]),
intensity: 1.0,
space: cfg.space || "view",
shadow: false,
shadowDirty: true,
getShadowViewMatrix: (function () {
const look = math.vec3();
const up = math.vec3([0, 1, 0]);
return function () {
if (self._shadowViewMatrixDirty) {
if (!self._shadowViewMatrix) {
self._shadowViewMatrix = math.identityMat4();
}
const dir = self._state.dir;
math.lookAtMat4v([-dir[0], -dir[1], -dir[2]], [0, 0, 0], up, self._shadowViewMatrix);
self._shadowViewMatrixDirty = false;
}
return self._shadowViewMatrix;
};
})(),
getShadowProjMatrix: function () {
if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
if (!self._shadowProjMatrix) {
self._shadowProjMatrix = math.identityMat4();
}
math.orthoMat4c(-10, 10, -10, 10, 0, 500.0, self._shadowProjMatrix);
self._shadowProjMatrixDirty = false;
}
return self._shadowProjMatrix;
},
getShadowRenderBuf: function () {
if (!self._shadowRenderBuf) {
self._shadowRenderBuf = new RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl, { size: [1024, 1024]});
}
return self._shadowRenderBuf;
}
});
this.dir = cfg.dir;
this.color = cfg.color;
this.intensity = cfg.intensity;
this.shadow = cfg.shadow;
this.scene._lightCreated(this);
}
/**
The direction in which the light is shining.
@property dir
@default [1.0, 1.0, 1.0]
@type Float32Array
*/
set dir(value) {
this._state.dir.set(value || [1.0, 1.0, 1.0]);
this._shadowViewMatrixDirty = true;
this._renderer.shadowsDirty();
}
get dir() {
return this._state.dir;
}
/**
The color of this DirLight.
@property color
@default [0.7, 0.7, 0.8]
@type Float32Array
*/
set color(value) {
this._state.color.set(value || [0.7, 0.7, 0.8]);
this._renderer.imageDirty();
}
get color() {
return this._state.color;
}
/**
The intensity of this DirLight.
Fires a {{#crossLink "DirLight/intensity:event"}}{{/crossLink}} event on change.
@property intensity
@default 1.0
@type Number
*/
set intensity(value) {
value = value !== undefined ? value : 1.0;
this._state.intensity = value;
this._renderer.imageDirty();
}
get intensity() {
return this._state.intensity;
}
/**
Flag which indicates if this DirLight casts a shadow.
@property shadow
@default false
@type Boolean
*/
set shadow(value) {
value = !!value;
if (this._state.shadow === value) {
return;
}
this._state.shadow = value;
this._shadowViewMatrixDirty = true;
this._renderer.shadowsDirty();
}
get shadow() {
return this._state.shadow;
}
destroy() {
super.destroy();
this._state.destroy();
if (this._shadowRenderBuf) {
this._shadowRenderBuf.destroy();
}
this.scene._lightDestroyed(this);
this._renderer.shadowsDirty();
}
}
componentClasses[type] = DirLight;
export {DirLight};