UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

396 lines (359 loc) 12.4 kB
import {Light} from './Light.js'; import {RenderState} from '../webgl/RenderState.js'; import {RenderBuffer} from '../webgl/RenderBuffer.js'; import {math} from '../math/math.js'; /** * A positional light source that originates from a single point and spreads outward in all directions, with optional attenuation over distance. * * * Has a position in {@link PointLight#pos}, but no direction. * * Defined in either *World* or *View* coordinate space. When in World-space, {@link PointLight#pos} is relative to * the World coordinate system, and will appear to move as the {@link Camera} moves. When in View-space, * {@link PointLight#pos} is relative to the View coordinate system, and will behave as if fixed to the viewer's head. * * Has {@link PointLight#constantAttenuation}, {@link PointLight#linearAttenuation} and {@link PointLight#quadraticAttenuation} * factors, which indicate how intensity attenuates over distance. * * {@link AmbientLight}s, {@link PointLight}s and {@link PointLight}s are registered by their {@link Component#id} on {@link Scene#lights}. * * ## Usage * * In the example below we'll replace the {@link Scene}'s default light sources with three World-space PointLights. * * [[Run this example](/examples/index.html#lights_PointLight_world)] * * ````javascript * import {Viewer, Mesh, buildSphereGeometry, buildPlaneGeometry, * ReadableGeometry, PhongMaterial, Texture, PointLight} from "xeokit-sdk.es.js"; * * // Create a Viewer and arrange the camera * * const viewer = new Viewer({ * canvasId: "myCanvas" * }); * * viewer.scene.camera.eye = [0, 0, 5]; * viewer.scene.camera.look = [0, 0, 0]; * viewer.scene.camera.up = [0, 1, 0]; * * // Replace the Scene's default lights with three custom world-space PointLights * * viewer.scene.clearLights(); * * new PointLight(viewer.scene,{ * id: "keyLight", * pos: [-80, 60, 80], * color: [1.0, 0.3, 0.3], * intensity: 1.0, * space: "world" * }); * * new PointLight(viewer.scene,{ * id: "fillLight", * pos: [80, 40, 40], * color: [0.3, 1.0, 0.3], * intensity: 1.0, * space: "world" * }); * * new PointLight(viewer.scene,{ * id: "rimLight", * pos: [-20, 80, -80], * color: [0.6, 0.6, 0.6], * intensity: 1.0, * space: "world" * }); * * // Create a sphere and ground plane * * new Mesh(viewer.scene, { * geometry: new ReadableGeometry(viewer.scene, buildSphereGeometry({ * radius: 1.3 * }), * material: new PhongMaterial(viewer.scene, { * diffuse: [0.7, 0.7, 0.7], * specular: [1.0, 1.0, 1.0], * emissive: [0, 0, 0], * alpha: 1.0, * ambient: [1, 1, 0], * diffuseMap: new Texture(viewer.scene, { * src: "textures/diffuse/uvGrid2.jpg" * }) * }) * }); * * new Mesh(viewer.scene, { * geometry: buildPlaneGeometry(ReadableGeometry, viewer.scene, { * xSize: 30, * zSize: 30 * }), * material: new PhongMaterial(viewer.scene, { * diffuseMap: new Texture(viewer.scene, { * src: "textures/diffuse/uvGrid2.jpg" * }), * backfaces: true * }), * position: [0, -2.1, 0] * }); * ```` */ class PointLight extends Light { /** @private */ get type() { return "PointLight"; } /** * @param {Component} owner Owner component. When destroyed, the owner will destroy this PointLight as well. * @param {*} [cfg] The PointLight configuration * @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Scene}, generated automatically when omitted. * @param {Number[]} [cfg.pos=[ 1.0, 1.0, 1.0 ]] Position, in either World or View space, depending on the value of the **space** parameter. * @param {Number[]} [cfg.color=[0.7, 0.7, 0.8 ]] Color of this PointLight. * @param {Number} [cfg.intensity=1.0] Intensity of this PointLight, as a factor in range ````[0..1]````. * @param {Number} [cfg.constantAttenuation=0] Constant attenuation factor. * @param {Number} [cfg.linearAttenuation=0] Linear attenuation factor. * @param {Number} [cfg.quadraticAttenuation=0]Quadratic attenuation factor. * @param {String} [cfg.space="view"]The coordinate system this PointLight is defined in - "view" or "world". * @param {Boolean} [cfg.castsShadow=false] Flag which indicates if this PointLight casts a castsShadow. */ constructor(owner, cfg = {}) { super(owner, cfg); const self = this; this._shadowRenderBuf = null; this._shadowViewMatrix = null; this._shadowProjMatrix = null; this._shadowViewMatrixDirty = true; this._shadowProjMatrixDirty = true; const camera = this.scene.camera; const canvas = this.scene.canvas; this._onCameraViewMatrix = camera.on("viewMatrix", () => { this._shadowViewMatrixDirty = true; }); this._onCameraProjMatrix = camera.on("projMatrix", () => { this._shadowProjMatrixDirty = true; }); this._onCanvasBoundary = canvas.on("boundary", () => { this._shadowProjMatrixDirty = true; }); this._state = new RenderState({ type: "point", pos: math.vec3([1.0, 1.0, 1.0]), color: math.vec3([0.7, 0.7, 0.8]), intensity: 1.0, attenuation: [0.0, 0.0, 0.0], space: cfg.space || "view", castsShadow: false, getShadowViewMatrix: () => { if (self._shadowViewMatrixDirty) { if (!self._shadowViewMatrix) { self._shadowViewMatrix = math.identityMat4(); } const eye = self._state.pos; const look = camera.look; const up = camera.up; math.lookAtMat4v(eye, look, up, self._shadowViewMatrix); self._shadowViewMatrixDirty = false; } return self._shadowViewMatrix; }, getShadowProjMatrix: () => { if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes if (!self._shadowProjMatrix) { self._shadowProjMatrix = math.identityMat4(); } const canvas = self.scene.canvas.canvas; math.perspectiveMat4(70 * (Math.PI / 180.0), canvas.clientWidth / canvas.clientHeight, 0.1, 500.0, self._shadowProjMatrix); self._shadowProjMatrixDirty = false; } return self._shadowProjMatrix; }, getShadowRenderBuf: () => { if (!self._shadowRenderBuf) { self._shadowRenderBuf = new RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl, {size: [1024, 1024]}); // Super old mobile devices have a limit of 1024x1024 textures } return self._shadowRenderBuf; } }); this.pos = cfg.pos; this.color = cfg.color; this.intensity = cfg.intensity; this.constantAttenuation = cfg.constantAttenuation; this.linearAttenuation = cfg.linearAttenuation; this.quadraticAttenuation = cfg.quadraticAttenuation; this.castsShadow = cfg.castsShadow; this.scene._lightCreated(this); } /** * Sets the position of this PointLight. * * This will be either World- or View-space, depending on the value of {@link PointLight#space}. * * Default value is ````[1.0, 1.0, 1.0]````. * * @param {Number[]} pos The position. */ set pos(pos) { this._state.pos.set(pos || [1.0, 1.0, 1.0]); this._shadowViewMatrixDirty = true; this.glRedraw(); } /** * Gets the position of this PointLight. * * This will be either World- or View-space, depending on the value of {@link PointLight#space}. * * Default value is ````[1.0, 1.0, 1.0]````. * * @returns {Number[]} The position. */ get pos() { return this._state.pos; } /** * Sets the RGB color of this PointLight. * * Default value is ````[0.7, 0.7, 0.8]````. * * @param {Number[]} color The PointLight's RGB color. */ set color(color) { this._state.color.set(color || [0.7, 0.7, 0.8]); this.glRedraw(); } /** * Gets the RGB color of this PointLight. * * Default value is ````[0.7, 0.7, 0.8]````. * * @returns {Number[]} The PointLight's RGB color. */ get color() { return this._state.color; } /** * Sets the intensity of this PointLight. * * Default intensity is ````1.0```` for maximum intensity. * * @param {Number} intensity The PointLight's intensity */ set intensity(intensity) { intensity = intensity !== undefined ? intensity : 1.0; this._state.intensity = intensity; this.glRedraw(); } /** * Gets the intensity of this PointLight. * * Default value is ````1.0```` for maximum intensity. * * @returns {Number} The PointLight's intensity. */ get intensity() { return this._state.intensity; } /** * Sets the constant attenuation factor for this PointLight. * * Default value is ````0````. * * @param {Number} value The constant attenuation factor. */ set constantAttenuation(value) { this._state.attenuation[0] = value || 0.0; this.glRedraw(); } /** * Gets the constant attenuation factor for this PointLight. * * Default value is ````0````. * * @returns {Number} The constant attenuation factor. */ get constantAttenuation() { return this._state.attenuation[0]; } /** * Sets the linear attenuation factor for this PointLight. * * Default value is ````0````. * * @param {Number} value The linear attenuation factor. */ set linearAttenuation(value) { this._state.attenuation[1] = value || 0.0; this.glRedraw(); } /** * Gets the linear attenuation factor for this PointLight. * * Default value is ````0````. * * @returns {Number} The linear attenuation factor. */ get linearAttenuation() { return this._state.attenuation[1]; } /** * Sets the quadratic attenuation factor for this PointLight. * * Default value is ````0````. * * @param {Number} value The quadratic attenuation factor. */ set quadraticAttenuation(value) { this._state.attenuation[2] = value || 0.0; this.glRedraw(); } /** * Gets the quadratic attenuation factor for this PointLight. * * Default value is ````0````. * * @returns {Number} The quadratic attenuation factor. */ get quadraticAttenuation() { return this._state.attenuation[2]; } /** * Sets if this PointLight casts a shadow. * * Default value is ````false````. * * @param {Boolean} castsShadow Set ````true```` to cast shadows. */ set castsShadow(castsShadow) { castsShadow = !!castsShadow; if (this._state.castsShadow === castsShadow) { return; } this._state.castsShadow = castsShadow; this._shadowViewMatrixDirty = true; this.glRedraw(); } /** * Gets if this PointLight casts a shadow. * * Default value is ````false````. * * @returns {Boolean} ````true```` if this PointLight casts shadows. */ get castsShadow() { return this._state.castsShadow; } /** * Destroys this PointLight. */ destroy() { const camera = this.scene.camera; const canvas = this.scene.canvas; camera.off(this._onCameraViewMatrix); camera.off(this._onCameraProjMatrix); canvas.off(this._onCanvasBoundary); super.destroy(); this._state.destroy(); if (this._shadowRenderBuf) { this._shadowRenderBuf.destroy(); } this.scene._lightDestroyed(this); this.glRedraw(); } } export {PointLight};