light.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. import { __decorate } from "../tslib.es6.js";
  2. import { serialize, serializeAsColor3, expandToProperty } from "../Misc/decorators.js";
  3. import { Vector3 } from "../Maths/math.vector.js";
  4. import { Color3, TmpColors } from "../Maths/math.color.js";
  5. import { Node } from "../node.js";
  6. import { UniformBuffer } from "../Materials/uniformBuffer.js";
  7. import { GetClass } from "../Misc/typeStore.js";
  8. import { LightConstants } from "./lightConstants.js";
  9. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  10. /**
  11. * Base class of all the lights in Babylon. It groups all the generic information about lights.
  12. * Lights are used, as you would expect, to affect how meshes are seen, in terms of both illumination and colour.
  13. * All meshes allow light to pass through them unless shadow generation is activated. The default number of lights allowed is four but this can be increased.
  14. */
  15. export class Light extends Node {
  16. /**
  17. * Defines how far from the source the light is impacting in scene units.
  18. * Note: Unused in PBR material as the distance light falloff is defined following the inverse squared falloff.
  19. */
  20. get range() {
  21. return this._range;
  22. }
  23. /**
  24. * Defines how far from the source the light is impacting in scene units.
  25. * Note: Unused in PBR material as the distance light falloff is defined following the inverse squared falloff.
  26. */
  27. set range(value) {
  28. this._range = value;
  29. this._inverseSquaredRange = 1.0 / (this.range * this.range);
  30. }
  31. /**
  32. * Gets the photometric scale used to interpret the intensity.
  33. * This is only relevant with PBR Materials where the light intensity can be defined in a physical way.
  34. */
  35. get intensityMode() {
  36. return this._intensityMode;
  37. }
  38. /**
  39. * Sets the photometric scale used to interpret the intensity.
  40. * This is only relevant with PBR Materials where the light intensity can be defined in a physical way.
  41. */
  42. set intensityMode(value) {
  43. this._intensityMode = value;
  44. this._computePhotometricScale();
  45. }
  46. /**
  47. * Gets the light radius used by PBR Materials to simulate soft area lights.
  48. */
  49. get radius() {
  50. return this._radius;
  51. }
  52. /**
  53. * sets the light radius used by PBR Materials to simulate soft area lights.
  54. */
  55. set radius(value) {
  56. this._radius = value;
  57. this._computePhotometricScale();
  58. }
  59. /**
  60. * Gets whether or not the shadows are enabled for this light. This can help turning off/on shadow without detaching
  61. * the current shadow generator.
  62. */
  63. get shadowEnabled() {
  64. return this._shadowEnabled;
  65. }
  66. /**
  67. * Sets whether or not the shadows are enabled for this light. This can help turning off/on shadow without detaching
  68. * the current shadow generator.
  69. */
  70. set shadowEnabled(value) {
  71. if (this._shadowEnabled === value) {
  72. return;
  73. }
  74. this._shadowEnabled = value;
  75. this._markMeshesAsLightDirty();
  76. }
  77. /**
  78. * Gets the only meshes impacted by this light.
  79. */
  80. get includedOnlyMeshes() {
  81. return this._includedOnlyMeshes;
  82. }
  83. /**
  84. * Sets the only meshes impacted by this light.
  85. */
  86. set includedOnlyMeshes(value) {
  87. this._includedOnlyMeshes = value;
  88. this._hookArrayForIncludedOnly(value);
  89. }
  90. /**
  91. * Gets the meshes not impacted by this light.
  92. */
  93. get excludedMeshes() {
  94. return this._excludedMeshes;
  95. }
  96. /**
  97. * Sets the meshes not impacted by this light.
  98. */
  99. set excludedMeshes(value) {
  100. this._excludedMeshes = value;
  101. this._hookArrayForExcluded(value);
  102. }
  103. /**
  104. * Gets the layer id use to find what meshes are not impacted by the light.
  105. * Inactive if 0
  106. */
  107. get excludeWithLayerMask() {
  108. return this._excludeWithLayerMask;
  109. }
  110. /**
  111. * Sets the layer id use to find what meshes are not impacted by the light.
  112. * Inactive if 0
  113. */
  114. set excludeWithLayerMask(value) {
  115. this._excludeWithLayerMask = value;
  116. this._resyncMeshes();
  117. }
  118. /**
  119. * Gets the layer id use to find what meshes are impacted by the light.
  120. * Inactive if 0
  121. */
  122. get includeOnlyWithLayerMask() {
  123. return this._includeOnlyWithLayerMask;
  124. }
  125. /**
  126. * Sets the layer id use to find what meshes are impacted by the light.
  127. * Inactive if 0
  128. */
  129. set includeOnlyWithLayerMask(value) {
  130. this._includeOnlyWithLayerMask = value;
  131. this._resyncMeshes();
  132. }
  133. /**
  134. * Gets the lightmap mode of this light (should be one of the constants defined by Light.LIGHTMAP_x)
  135. */
  136. get lightmapMode() {
  137. return this._lightmapMode;
  138. }
  139. /**
  140. * Sets the lightmap mode of this light (should be one of the constants defined by Light.LIGHTMAP_x)
  141. */
  142. set lightmapMode(value) {
  143. if (this._lightmapMode === value) {
  144. return;
  145. }
  146. this._lightmapMode = value;
  147. this._markMeshesAsLightDirty();
  148. }
  149. /**
  150. * Returns the view matrix.
  151. * @param _faceIndex The index of the face for which we want to extract the view matrix. Only used for point light types.
  152. * @returns The view matrix. Can be null, if a view matrix cannot be defined for the type of light considered (as for a hemispherical light, for example).
  153. */
  154. getViewMatrix(_faceIndex) {
  155. return null;
  156. }
  157. /**
  158. * Returns the projection matrix.
  159. * Note that viewMatrix and renderList are optional and are only used by lights that calculate the projection matrix from a list of meshes (e.g. directional lights with automatic extents calculation).
  160. * @param _viewMatrix The view transform matrix of the light (optional).
  161. * @param _renderList The list of meshes to take into account when calculating the projection matrix (optional).
  162. * @returns The projection matrix. Can be null, if a projection matrix cannot be defined for the type of light considered (as for a hemispherical light, for example).
  163. */
  164. getProjectionMatrix(_viewMatrix, _renderList) {
  165. return null;
  166. }
  167. /**
  168. * Creates a Light object in the scene.
  169. * Documentation : https://doc.babylonjs.com/features/featuresDeepDive/lights/lights_introduction
  170. * @param name The friendly name of the light
  171. * @param scene The scene the light belongs too
  172. */
  173. constructor(name, scene) {
  174. super(name, scene);
  175. /**
  176. * Diffuse gives the basic color to an object.
  177. */
  178. this.diffuse = new Color3(1.0, 1.0, 1.0);
  179. /**
  180. * Specular produces a highlight color on an object.
  181. * Note: This is not affecting PBR materials.
  182. */
  183. this.specular = new Color3(1.0, 1.0, 1.0);
  184. /**
  185. * Defines the falloff type for this light. This lets overriding how punctual light are
  186. * falling off base on range or angle.
  187. * This can be set to any values in Light.FALLOFF_x.
  188. *
  189. * Note: This is only useful for PBR Materials at the moment. This could be extended if required to
  190. * other types of materials.
  191. */
  192. this.falloffType = Light.FALLOFF_DEFAULT;
  193. /**
  194. * Strength of the light.
  195. * Note: By default it is define in the framework own unit.
  196. * Note: In PBR materials the intensityMode can be use to chose what unit the intensity is defined in.
  197. */
  198. this.intensity = 1.0;
  199. this._range = Number.MAX_VALUE;
  200. this._inverseSquaredRange = 0;
  201. /**
  202. * Cached photometric scale default to 1.0 as the automatic intensity mode defaults to 1.0 for every type
  203. * of light.
  204. */
  205. this._photometricScale = 1.0;
  206. this._intensityMode = Light.INTENSITYMODE_AUTOMATIC;
  207. this._radius = 0.00001;
  208. /**
  209. * Defines the rendering priority of the lights. It can help in case of fallback or number of lights
  210. * exceeding the number allowed of the materials.
  211. */
  212. this.renderPriority = 0;
  213. this._shadowEnabled = true;
  214. this._excludeWithLayerMask = 0;
  215. this._includeOnlyWithLayerMask = 0;
  216. this._lightmapMode = 0;
  217. /**
  218. * Shadow generators associated to the light.
  219. * @internal Internal use only.
  220. */
  221. this._shadowGenerators = null;
  222. /**
  223. * @internal Internal use only.
  224. */
  225. this._excludedMeshesIds = new Array();
  226. /**
  227. * @internal Internal use only.
  228. */
  229. this._includedOnlyMeshesIds = new Array();
  230. /** @internal */
  231. this._isLight = true;
  232. this.getScene().addLight(this);
  233. this._uniformBuffer = new UniformBuffer(this.getScene().getEngine(), undefined, undefined, name);
  234. this._buildUniformLayout();
  235. this.includedOnlyMeshes = [];
  236. this.excludedMeshes = [];
  237. this._resyncMeshes();
  238. }
  239. /**
  240. * Sets the passed Effect "effect" with the Light textures.
  241. * @param effect The effect to update
  242. * @param lightIndex The index of the light in the effect to update
  243. * @returns The light
  244. */
  245. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  246. transferTexturesToEffect(effect, lightIndex) {
  247. // Do nothing by default.
  248. return this;
  249. }
  250. /**
  251. * Binds the lights information from the scene to the effect for the given mesh.
  252. * @param lightIndex Light index
  253. * @param scene The scene where the light belongs to
  254. * @param effect The effect we are binding the data to
  255. * @param useSpecular Defines if specular is supported
  256. * @param receiveShadows Defines if the effect (mesh) we bind the light for receives shadows
  257. */
  258. _bindLight(lightIndex, scene, effect, useSpecular, receiveShadows = true) {
  259. const iAsString = lightIndex.toString();
  260. let needUpdate = false;
  261. this._uniformBuffer.bindToEffect(effect, "Light" + iAsString);
  262. if (this._renderId !== scene.getRenderId() || this._lastUseSpecular !== useSpecular || !this._uniformBuffer.useUbo) {
  263. this._renderId = scene.getRenderId();
  264. this._lastUseSpecular = useSpecular;
  265. const scaledIntensity = this.getScaledIntensity();
  266. this.transferToEffect(effect, iAsString);
  267. this.diffuse.scaleToRef(scaledIntensity, TmpColors.Color3[0]);
  268. this._uniformBuffer.updateColor4("vLightDiffuse", TmpColors.Color3[0], this.range, iAsString);
  269. if (useSpecular) {
  270. this.specular.scaleToRef(scaledIntensity, TmpColors.Color3[1]);
  271. this._uniformBuffer.updateColor4("vLightSpecular", TmpColors.Color3[1], this.radius, iAsString);
  272. }
  273. needUpdate = true;
  274. }
  275. // Textures might still need to be rebound.
  276. this.transferTexturesToEffect(effect, iAsString);
  277. // Shadows
  278. if (scene.shadowsEnabled && this.shadowEnabled && receiveShadows) {
  279. const shadowGenerator = this.getShadowGenerator(scene.activeCamera) ?? this.getShadowGenerator();
  280. if (shadowGenerator) {
  281. shadowGenerator.bindShadowLight(iAsString, effect);
  282. needUpdate = true;
  283. }
  284. }
  285. if (needUpdate) {
  286. this._uniformBuffer.update();
  287. }
  288. else {
  289. this._uniformBuffer.bindUniformBuffer();
  290. }
  291. }
  292. /**
  293. * Returns the string "Light".
  294. * @returns the class name
  295. */
  296. getClassName() {
  297. return "Light";
  298. }
  299. /**
  300. * Converts the light information to a readable string for debug purpose.
  301. * @param fullDetails Supports for multiple levels of logging within scene loading
  302. * @returns the human readable light info
  303. */
  304. toString(fullDetails) {
  305. let ret = "Name: " + this.name;
  306. ret += ", type: " + ["Point", "Directional", "Spot", "Hemispheric"][this.getTypeID()];
  307. if (this.animations) {
  308. for (let i = 0; i < this.animations.length; i++) {
  309. ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
  310. }
  311. }
  312. return ret;
  313. }
  314. /** @internal */
  315. _syncParentEnabledState() {
  316. super._syncParentEnabledState();
  317. if (!this.isDisposed()) {
  318. this._resyncMeshes();
  319. }
  320. }
  321. /**
  322. * Set the enabled state of this node.
  323. * @param value - the new enabled state
  324. */
  325. setEnabled(value) {
  326. super.setEnabled(value);
  327. this._resyncMeshes();
  328. }
  329. /**
  330. * Returns the Light associated shadow generator if any.
  331. * @param camera Camera for which the shadow generator should be retrieved (default: null). If null, retrieves the default shadow generator
  332. * @returns the associated shadow generator.
  333. */
  334. getShadowGenerator(camera = null) {
  335. if (this._shadowGenerators === null) {
  336. return null;
  337. }
  338. return this._shadowGenerators.get(camera) ?? null;
  339. }
  340. /**
  341. * Returns all the shadow generators associated to this light
  342. * @returns
  343. */
  344. getShadowGenerators() {
  345. return this._shadowGenerators;
  346. }
  347. /**
  348. * Returns a Vector3, the absolute light position in the World.
  349. * @returns the world space position of the light
  350. */
  351. getAbsolutePosition() {
  352. return Vector3.Zero();
  353. }
  354. /**
  355. * Specifies if the light will affect the passed mesh.
  356. * @param mesh The mesh to test against the light
  357. * @returns true the mesh is affected otherwise, false.
  358. */
  359. canAffectMesh(mesh) {
  360. if (!mesh) {
  361. return true;
  362. }
  363. if (this.includedOnlyMeshes && this.includedOnlyMeshes.length > 0 && this.includedOnlyMeshes.indexOf(mesh) === -1) {
  364. return false;
  365. }
  366. if (this.excludedMeshes && this.excludedMeshes.length > 0 && this.excludedMeshes.indexOf(mesh) !== -1) {
  367. return false;
  368. }
  369. if (this.includeOnlyWithLayerMask !== 0 && (this.includeOnlyWithLayerMask & mesh.layerMask) === 0) {
  370. return false;
  371. }
  372. if (this.excludeWithLayerMask !== 0 && this.excludeWithLayerMask & mesh.layerMask) {
  373. return false;
  374. }
  375. return true;
  376. }
  377. /**
  378. * Releases resources associated with this node.
  379. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  380. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  381. */
  382. dispose(doNotRecurse, disposeMaterialAndTextures = false) {
  383. if (this._shadowGenerators) {
  384. const iterator = this._shadowGenerators.values();
  385. for (let key = iterator.next(); key.done !== true; key = iterator.next()) {
  386. const shadowGenerator = key.value;
  387. shadowGenerator.dispose();
  388. }
  389. this._shadowGenerators = null;
  390. }
  391. // Animations
  392. this.getScene().stopAnimation(this);
  393. if (this._parentContainer) {
  394. const index = this._parentContainer.lights.indexOf(this);
  395. if (index > -1) {
  396. this._parentContainer.lights.splice(index, 1);
  397. }
  398. this._parentContainer = null;
  399. }
  400. // Remove from meshes
  401. for (const mesh of this.getScene().meshes) {
  402. mesh._removeLightSource(this, true);
  403. }
  404. this._uniformBuffer.dispose();
  405. // Remove from scene
  406. this.getScene().removeLight(this);
  407. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  408. }
  409. /**
  410. * Returns the light type ID (integer).
  411. * @returns The light Type id as a constant defines in Light.LIGHTTYPEID_x
  412. */
  413. getTypeID() {
  414. return 0;
  415. }
  416. /**
  417. * Returns the intensity scaled by the Photometric Scale according to the light type and intensity mode.
  418. * @returns the scaled intensity in intensity mode unit
  419. */
  420. getScaledIntensity() {
  421. return this._photometricScale * this.intensity;
  422. }
  423. /**
  424. * Returns a new Light object, named "name", from the current one.
  425. * @param name The name of the cloned light
  426. * @param newParent The parent of this light, if it has one
  427. * @returns the new created light
  428. */
  429. clone(name, newParent = null) {
  430. const constructor = Light.GetConstructorFromName(this.getTypeID(), name, this.getScene());
  431. if (!constructor) {
  432. return null;
  433. }
  434. const clonedLight = SerializationHelper.Clone(constructor, this);
  435. if (name) {
  436. clonedLight.name = name;
  437. }
  438. if (newParent) {
  439. clonedLight.parent = newParent;
  440. }
  441. clonedLight.setEnabled(this.isEnabled());
  442. this.onClonedObservable.notifyObservers(clonedLight);
  443. return clonedLight;
  444. }
  445. /**
  446. * Serializes the current light into a Serialization object.
  447. * @returns the serialized object.
  448. */
  449. serialize() {
  450. const serializationObject = SerializationHelper.Serialize(this);
  451. serializationObject.uniqueId = this.uniqueId;
  452. // Type
  453. serializationObject.type = this.getTypeID();
  454. // Parent
  455. if (this.parent) {
  456. this.parent._serializeAsParent(serializationObject);
  457. }
  458. // Inclusion / exclusions
  459. if (this.excludedMeshes.length > 0) {
  460. serializationObject.excludedMeshesIds = [];
  461. this.excludedMeshes.forEach((mesh) => {
  462. serializationObject.excludedMeshesIds.push(mesh.id);
  463. });
  464. }
  465. if (this.includedOnlyMeshes.length > 0) {
  466. serializationObject.includedOnlyMeshesIds = [];
  467. this.includedOnlyMeshes.forEach((mesh) => {
  468. serializationObject.includedOnlyMeshesIds.push(mesh.id);
  469. });
  470. }
  471. // Animations
  472. SerializationHelper.AppendSerializedAnimations(this, serializationObject);
  473. serializationObject.ranges = this.serializeAnimationRanges();
  474. serializationObject.isEnabled = this.isEnabled();
  475. return serializationObject;
  476. }
  477. /**
  478. * Creates a new typed light from the passed type (integer) : point light = 0, directional light = 1, spot light = 2, hemispheric light = 3.
  479. * This new light is named "name" and added to the passed scene.
  480. * @param type Type according to the types available in Light.LIGHTTYPEID_x
  481. * @param name The friendly name of the light
  482. * @param scene The scene the new light will belong to
  483. * @returns the constructor function
  484. */
  485. static GetConstructorFromName(type, name, scene) {
  486. const constructorFunc = Node.Construct("Light_Type_" + type, name, scene);
  487. if (constructorFunc) {
  488. return constructorFunc;
  489. }
  490. // Default to no light for none present once.
  491. return null;
  492. }
  493. /**
  494. * Parses the passed "parsedLight" and returns a new instanced Light from this parsing.
  495. * @param parsedLight The JSON representation of the light
  496. * @param scene The scene to create the parsed light in
  497. * @returns the created light after parsing
  498. */
  499. static Parse(parsedLight, scene) {
  500. const constructor = Light.GetConstructorFromName(parsedLight.type, parsedLight.name, scene);
  501. if (!constructor) {
  502. return null;
  503. }
  504. const light = SerializationHelper.Parse(constructor, parsedLight, scene);
  505. // Inclusion / exclusions
  506. if (parsedLight.excludedMeshesIds) {
  507. light._excludedMeshesIds = parsedLight.excludedMeshesIds;
  508. }
  509. if (parsedLight.includedOnlyMeshesIds) {
  510. light._includedOnlyMeshesIds = parsedLight.includedOnlyMeshesIds;
  511. }
  512. // Parent
  513. if (parsedLight.parentId !== undefined) {
  514. light._waitingParentId = parsedLight.parentId;
  515. }
  516. if (parsedLight.parentInstanceIndex !== undefined) {
  517. light._waitingParentInstanceIndex = parsedLight.parentInstanceIndex;
  518. }
  519. // Falloff
  520. if (parsedLight.falloffType !== undefined) {
  521. light.falloffType = parsedLight.falloffType;
  522. }
  523. // Lightmaps
  524. if (parsedLight.lightmapMode !== undefined) {
  525. light.lightmapMode = parsedLight.lightmapMode;
  526. }
  527. // Animations
  528. if (parsedLight.animations) {
  529. for (let animationIndex = 0; animationIndex < parsedLight.animations.length; animationIndex++) {
  530. const parsedAnimation = parsedLight.animations[animationIndex];
  531. const internalClass = GetClass("BABYLON.Animation");
  532. if (internalClass) {
  533. light.animations.push(internalClass.Parse(parsedAnimation));
  534. }
  535. }
  536. Node.ParseAnimationRanges(light, parsedLight, scene);
  537. }
  538. if (parsedLight.autoAnimate) {
  539. scene.beginAnimation(light, parsedLight.autoAnimateFrom, parsedLight.autoAnimateTo, parsedLight.autoAnimateLoop, parsedLight.autoAnimateSpeed || 1.0);
  540. }
  541. // Check if isEnabled is defined to be back compatible with prior serialized versions.
  542. if (parsedLight.isEnabled !== undefined) {
  543. light.setEnabled(parsedLight.isEnabled);
  544. }
  545. return light;
  546. }
  547. _hookArrayForExcluded(array) {
  548. const oldPush = array.push;
  549. array.push = (...items) => {
  550. const result = oldPush.apply(array, items);
  551. for (const item of items) {
  552. item._resyncLightSource(this);
  553. }
  554. return result;
  555. };
  556. const oldSplice = array.splice;
  557. array.splice = (index, deleteCount) => {
  558. const deleted = oldSplice.apply(array, [index, deleteCount]);
  559. for (const item of deleted) {
  560. item._resyncLightSource(this);
  561. }
  562. return deleted;
  563. };
  564. for (const item of array) {
  565. item._resyncLightSource(this);
  566. }
  567. }
  568. _hookArrayForIncludedOnly(array) {
  569. const oldPush = array.push;
  570. array.push = (...items) => {
  571. const result = oldPush.apply(array, items);
  572. this._resyncMeshes();
  573. return result;
  574. };
  575. const oldSplice = array.splice;
  576. array.splice = (index, deleteCount) => {
  577. const deleted = oldSplice.apply(array, [index, deleteCount]);
  578. this._resyncMeshes();
  579. return deleted;
  580. };
  581. this._resyncMeshes();
  582. }
  583. _resyncMeshes() {
  584. for (const mesh of this.getScene().meshes) {
  585. mesh._resyncLightSource(this);
  586. }
  587. }
  588. /**
  589. * Forces the meshes to update their light related information in their rendering used effects
  590. * @internal Internal Use Only
  591. */
  592. _markMeshesAsLightDirty() {
  593. for (const mesh of this.getScene().meshes) {
  594. if (mesh.lightSources.indexOf(this) !== -1) {
  595. mesh._markSubMeshesAsLightDirty();
  596. }
  597. }
  598. }
  599. /**
  600. * Recomputes the cached photometric scale if needed.
  601. */
  602. _computePhotometricScale() {
  603. this._photometricScale = this._getPhotometricScale();
  604. this.getScene().resetCachedMaterial();
  605. }
  606. /**
  607. * @returns the Photometric Scale according to the light type and intensity mode.
  608. */
  609. _getPhotometricScale() {
  610. let photometricScale = 0.0;
  611. const lightTypeID = this.getTypeID();
  612. //get photometric mode
  613. let photometricMode = this.intensityMode;
  614. if (photometricMode === Light.INTENSITYMODE_AUTOMATIC) {
  615. if (lightTypeID === Light.LIGHTTYPEID_DIRECTIONALLIGHT) {
  616. photometricMode = Light.INTENSITYMODE_ILLUMINANCE;
  617. }
  618. else {
  619. photometricMode = Light.INTENSITYMODE_LUMINOUSINTENSITY;
  620. }
  621. }
  622. //compute photometric scale
  623. switch (lightTypeID) {
  624. case Light.LIGHTTYPEID_POINTLIGHT:
  625. case Light.LIGHTTYPEID_SPOTLIGHT:
  626. switch (photometricMode) {
  627. case Light.INTENSITYMODE_LUMINOUSPOWER:
  628. photometricScale = 1.0 / (4.0 * Math.PI);
  629. break;
  630. case Light.INTENSITYMODE_LUMINOUSINTENSITY:
  631. photometricScale = 1.0;
  632. break;
  633. case Light.INTENSITYMODE_LUMINANCE:
  634. photometricScale = this.radius * this.radius;
  635. break;
  636. }
  637. break;
  638. case Light.LIGHTTYPEID_DIRECTIONALLIGHT:
  639. switch (photometricMode) {
  640. case Light.INTENSITYMODE_ILLUMINANCE:
  641. photometricScale = 1.0;
  642. break;
  643. case Light.INTENSITYMODE_LUMINANCE: {
  644. // When radius (and therefore solid angle) is non-zero a directional lights brightness can be specified via central (peak) luminance.
  645. // For a directional light the 'radius' defines the angular radius (in radians) rather than world-space radius (e.g. in metres).
  646. let apexAngleRadians = this.radius;
  647. // Impose a minimum light angular size to avoid the light becoming an infinitely small angular light source (i.e. a dirac delta function).
  648. apexAngleRadians = Math.max(apexAngleRadians, 0.001);
  649. const solidAngle = 2.0 * Math.PI * (1.0 - Math.cos(apexAngleRadians));
  650. photometricScale = solidAngle;
  651. break;
  652. }
  653. }
  654. break;
  655. case Light.LIGHTTYPEID_HEMISPHERICLIGHT:
  656. // No fall off in hemispheric light.
  657. photometricScale = 1.0;
  658. break;
  659. }
  660. return photometricScale;
  661. }
  662. /**
  663. * Reorder the light in the scene according to their defined priority.
  664. * @internal Internal Use Only
  665. */
  666. _reorderLightsInScene() {
  667. const scene = this.getScene();
  668. if (this._renderPriority != 0) {
  669. scene.requireLightSorting = true;
  670. }
  671. this.getScene().sortLightsByPriority();
  672. }
  673. }
  674. /**
  675. * Falloff Default: light is falling off following the material specification:
  676. * standard material is using standard falloff whereas pbr material can request special falloff per materials.
  677. */
  678. Light.FALLOFF_DEFAULT = LightConstants.FALLOFF_DEFAULT;
  679. /**
  680. * Falloff Physical: light is falling off following the inverse squared distance law.
  681. */
  682. Light.FALLOFF_PHYSICAL = LightConstants.FALLOFF_PHYSICAL;
  683. /**
  684. * Falloff gltf: light is falling off as described in the gltf moving to PBR document
  685. * to enhance interoperability with other engines.
  686. */
  687. Light.FALLOFF_GLTF = LightConstants.FALLOFF_GLTF;
  688. /**
  689. * Falloff Standard: light is falling off like in the standard material
  690. * to enhance interoperability with other materials.
  691. */
  692. Light.FALLOFF_STANDARD = LightConstants.FALLOFF_STANDARD;
  693. //lightmapMode Consts
  694. /**
  695. * If every light affecting the material is in this lightmapMode,
  696. * material.lightmapTexture adds or multiplies
  697. * (depends on material.useLightmapAsShadowmap)
  698. * after every other light calculations.
  699. */
  700. Light.LIGHTMAP_DEFAULT = LightConstants.LIGHTMAP_DEFAULT;
  701. /**
  702. * material.lightmapTexture as only diffuse lighting from this light
  703. * adds only specular lighting from this light
  704. * adds dynamic shadows
  705. */
  706. Light.LIGHTMAP_SPECULAR = LightConstants.LIGHTMAP_SPECULAR;
  707. /**
  708. * material.lightmapTexture as only lighting
  709. * no light calculation from this light
  710. * only adds dynamic shadows from this light
  711. */
  712. Light.LIGHTMAP_SHADOWSONLY = LightConstants.LIGHTMAP_SHADOWSONLY;
  713. // Intensity Mode Consts
  714. /**
  715. * Each light type uses the default quantity according to its type:
  716. * point/spot lights use luminous intensity
  717. * directional lights use illuminance
  718. */
  719. Light.INTENSITYMODE_AUTOMATIC = LightConstants.INTENSITYMODE_AUTOMATIC;
  720. /**
  721. * lumen (lm)
  722. */
  723. Light.INTENSITYMODE_LUMINOUSPOWER = LightConstants.INTENSITYMODE_LUMINOUSPOWER;
  724. /**
  725. * candela (lm/sr)
  726. */
  727. Light.INTENSITYMODE_LUMINOUSINTENSITY = LightConstants.INTENSITYMODE_LUMINOUSINTENSITY;
  728. /**
  729. * lux (lm/m^2)
  730. */
  731. Light.INTENSITYMODE_ILLUMINANCE = LightConstants.INTENSITYMODE_ILLUMINANCE;
  732. /**
  733. * nit (cd/m^2)
  734. */
  735. Light.INTENSITYMODE_LUMINANCE = LightConstants.INTENSITYMODE_LUMINANCE;
  736. // Light types ids const.
  737. /**
  738. * Light type const id of the point light.
  739. */
  740. Light.LIGHTTYPEID_POINTLIGHT = LightConstants.LIGHTTYPEID_POINTLIGHT;
  741. /**
  742. * Light type const id of the directional light.
  743. */
  744. Light.LIGHTTYPEID_DIRECTIONALLIGHT = LightConstants.LIGHTTYPEID_DIRECTIONALLIGHT;
  745. /**
  746. * Light type const id of the spot light.
  747. */
  748. Light.LIGHTTYPEID_SPOTLIGHT = LightConstants.LIGHTTYPEID_SPOTLIGHT;
  749. /**
  750. * Light type const id of the hemispheric light.
  751. */
  752. Light.LIGHTTYPEID_HEMISPHERICLIGHT = LightConstants.LIGHTTYPEID_HEMISPHERICLIGHT;
  753. __decorate([
  754. serializeAsColor3()
  755. ], Light.prototype, "diffuse", void 0);
  756. __decorate([
  757. serializeAsColor3()
  758. ], Light.prototype, "specular", void 0);
  759. __decorate([
  760. serialize()
  761. ], Light.prototype, "falloffType", void 0);
  762. __decorate([
  763. serialize()
  764. ], Light.prototype, "intensity", void 0);
  765. __decorate([
  766. serialize()
  767. ], Light.prototype, "range", null);
  768. __decorate([
  769. serialize()
  770. ], Light.prototype, "intensityMode", null);
  771. __decorate([
  772. serialize()
  773. ], Light.prototype, "radius", null);
  774. __decorate([
  775. serialize()
  776. ], Light.prototype, "_renderPriority", void 0);
  777. __decorate([
  778. expandToProperty("_reorderLightsInScene")
  779. ], Light.prototype, "renderPriority", void 0);
  780. __decorate([
  781. serialize("shadowEnabled")
  782. ], Light.prototype, "_shadowEnabled", void 0);
  783. __decorate([
  784. serialize("excludeWithLayerMask")
  785. ], Light.prototype, "_excludeWithLayerMask", void 0);
  786. __decorate([
  787. serialize("includeOnlyWithLayerMask")
  788. ], Light.prototype, "_includeOnlyWithLayerMask", void 0);
  789. __decorate([
  790. serialize("lightmapMode")
  791. ], Light.prototype, "_lightmapMode", void 0);
  792. //# sourceMappingURL=light.js.map