shadowLight.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { __decorate } from "../tslib.es6.js";
  2. import { serialize, serializeAsVector3 } from "../Misc/decorators.js";
  3. import { Matrix, TmpVectors, Vector3 } from "../Maths/math.vector.js";
  4. import { Light } from "./light.js";
  5. import { Axis } from "../Maths/math.axis.js";
  6. /**
  7. * Base implementation IShadowLight
  8. * It groups all the common behaviour in order to reduce duplication and better follow the DRY pattern.
  9. */
  10. export class ShadowLight extends Light {
  11. constructor() {
  12. super(...arguments);
  13. this._needProjectionMatrixCompute = true;
  14. this._viewMatrix = Matrix.Identity();
  15. this._projectionMatrix = Matrix.Identity();
  16. }
  17. _setPosition(value) {
  18. this._position = value;
  19. }
  20. /**
  21. * Sets the position the shadow will be casted from. Also use as the light position for both
  22. * point and spot lights.
  23. */
  24. get position() {
  25. return this._position;
  26. }
  27. /**
  28. * Sets the position the shadow will be casted from. Also use as the light position for both
  29. * point and spot lights.
  30. */
  31. set position(value) {
  32. this._setPosition(value);
  33. }
  34. _setDirection(value) {
  35. this._direction = value;
  36. }
  37. /**
  38. * In 2d mode (needCube being false), gets the direction used to cast the shadow.
  39. * Also use as the light direction on spot and directional lights.
  40. */
  41. get direction() {
  42. return this._direction;
  43. }
  44. /**
  45. * In 2d mode (needCube being false), sets the direction used to cast the shadow.
  46. * Also use as the light direction on spot and directional lights.
  47. */
  48. set direction(value) {
  49. this._setDirection(value);
  50. }
  51. /**
  52. * Gets the shadow projection clipping minimum z value.
  53. */
  54. get shadowMinZ() {
  55. return this._shadowMinZ;
  56. }
  57. /**
  58. * Sets the shadow projection clipping minimum z value.
  59. */
  60. set shadowMinZ(value) {
  61. this._shadowMinZ = value;
  62. this.forceProjectionMatrixCompute();
  63. }
  64. /**
  65. * Sets the shadow projection clipping maximum z value.
  66. */
  67. get shadowMaxZ() {
  68. return this._shadowMaxZ;
  69. }
  70. /**
  71. * Gets the shadow projection clipping maximum z value.
  72. */
  73. set shadowMaxZ(value) {
  74. this._shadowMaxZ = value;
  75. this.forceProjectionMatrixCompute();
  76. }
  77. /**
  78. * Computes the transformed information (transformedPosition and transformedDirection in World space) of the current light
  79. * @returns true if the information has been computed, false if it does not need to (no parenting)
  80. */
  81. computeTransformedInformation() {
  82. if (this.parent && this.parent.getWorldMatrix) {
  83. if (!this.transformedPosition) {
  84. this.transformedPosition = Vector3.Zero();
  85. }
  86. Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
  87. // In case the direction is present.
  88. if (this.direction) {
  89. if (!this.transformedDirection) {
  90. this.transformedDirection = Vector3.Zero();
  91. }
  92. Vector3.TransformNormalToRef(this.direction, this.parent.getWorldMatrix(), this.transformedDirection);
  93. }
  94. return true;
  95. }
  96. return false;
  97. }
  98. /**
  99. * Return the depth scale used for the shadow map.
  100. * @returns the depth scale.
  101. */
  102. getDepthScale() {
  103. return 50.0;
  104. }
  105. /**
  106. * Get the direction to use to render the shadow map. In case of cube texture, the face index can be passed.
  107. * @param faceIndex The index of the face we are computed the direction to generate shadow
  108. * @returns The set direction in 2d mode otherwise the direction to the cubemap face if needCube() is true
  109. */
  110. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  111. getShadowDirection(faceIndex) {
  112. return this.transformedDirection ? this.transformedDirection : this.direction;
  113. }
  114. /**
  115. * If computeTransformedInformation has been called, returns the ShadowLight absolute position in the world. Otherwise, returns the local position.
  116. * @returns the position vector in world space
  117. */
  118. getAbsolutePosition() {
  119. return this.transformedPosition ? this.transformedPosition : this.position;
  120. }
  121. /**
  122. * Sets the ShadowLight direction toward the passed target.
  123. * @param target The point to target in local space
  124. * @returns the updated ShadowLight direction
  125. */
  126. setDirectionToTarget(target) {
  127. this.direction = Vector3.Normalize(target.subtract(this.position));
  128. return this.direction;
  129. }
  130. /**
  131. * Returns the light rotation in euler definition.
  132. * @returns the x y z rotation in local space.
  133. */
  134. getRotation() {
  135. this.direction.normalize();
  136. const xaxis = Vector3.Cross(this.direction, Axis.Y);
  137. const yaxis = Vector3.Cross(xaxis, this.direction);
  138. return Vector3.RotationFromAxis(xaxis, yaxis, this.direction);
  139. }
  140. /**
  141. * Returns whether or not the shadow generation require a cube texture or a 2d texture.
  142. * @returns true if a cube texture needs to be use
  143. */
  144. needCube() {
  145. return false;
  146. }
  147. /**
  148. * Detects if the projection matrix requires to be recomputed this frame.
  149. * @returns true if it requires to be recomputed otherwise, false.
  150. */
  151. needProjectionMatrixCompute() {
  152. return this._needProjectionMatrixCompute;
  153. }
  154. /**
  155. * Forces the shadow generator to recompute the projection matrix even if position and direction did not changed.
  156. */
  157. forceProjectionMatrixCompute() {
  158. this._needProjectionMatrixCompute = true;
  159. }
  160. /** @internal */
  161. _initCache() {
  162. super._initCache();
  163. this._cache.position = Vector3.Zero();
  164. }
  165. /** @internal */
  166. _isSynchronized() {
  167. if (!this._cache.position.equals(this.position)) {
  168. return false;
  169. }
  170. return true;
  171. }
  172. /**
  173. * Computes the world matrix of the node
  174. * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
  175. * @returns the world matrix
  176. */
  177. computeWorldMatrix(force) {
  178. if (!force && this.isSynchronized()) {
  179. this._currentRenderId = this.getScene().getRenderId();
  180. return this._worldMatrix;
  181. }
  182. this._updateCache();
  183. this._cache.position.copyFrom(this.position);
  184. if (!this._worldMatrix) {
  185. this._worldMatrix = Matrix.Identity();
  186. }
  187. Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
  188. if (this.parent && this.parent.getWorldMatrix) {
  189. this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
  190. this._markSyncedWithParent();
  191. }
  192. // Cache the determinant
  193. this._worldMatrixDeterminantIsDirty = true;
  194. return this._worldMatrix;
  195. }
  196. /**
  197. * Gets the minZ used for shadow according to both the scene and the light.
  198. * @param activeCamera The camera we are returning the min for
  199. * @returns the depth min z
  200. */
  201. getDepthMinZ(activeCamera) {
  202. return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ;
  203. }
  204. /**
  205. * Gets the maxZ used for shadow according to both the scene and the light.
  206. * @param activeCamera The camera we are returning the max for
  207. * @returns the depth max z
  208. */
  209. getDepthMaxZ(activeCamera) {
  210. return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ;
  211. }
  212. /**
  213. * Sets the shadow projection matrix in parameter to the generated projection matrix.
  214. * @param matrix The matrix to updated with the projection information
  215. * @param viewMatrix The transform matrix of the light
  216. * @param renderList The list of mesh to render in the map
  217. * @returns The current light
  218. */
  219. setShadowProjectionMatrix(matrix, viewMatrix, renderList) {
  220. if (this.customProjectionMatrixBuilder) {
  221. this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
  222. }
  223. else {
  224. this._setDefaultShadowProjectionMatrix(matrix, viewMatrix, renderList);
  225. }
  226. return this;
  227. }
  228. /** @internal */
  229. _syncParentEnabledState() {
  230. super._syncParentEnabledState();
  231. if (!this.parent || !this.parent.getWorldMatrix) {
  232. this.transformedPosition = null;
  233. this.transformedDirection = null;
  234. }
  235. }
  236. /**
  237. * Returns the view matrix.
  238. * @param faceIndex The index of the face for which we want to extract the view matrix. Only used for point light types.
  239. * @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).
  240. */
  241. getViewMatrix(faceIndex) {
  242. const lightDirection = TmpVectors.Vector3[0];
  243. let lightPosition = this.position;
  244. if (this.computeTransformedInformation()) {
  245. lightPosition = this.transformedPosition;
  246. }
  247. Vector3.NormalizeToRef(this.getShadowDirection(faceIndex), lightDirection);
  248. if (Math.abs(Vector3.Dot(lightDirection, Vector3.Up())) === 1.0) {
  249. lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  250. }
  251. const lightTarget = TmpVectors.Vector3[1];
  252. lightPosition.addToRef(lightDirection, lightTarget);
  253. Matrix.LookAtLHToRef(lightPosition, lightTarget, Vector3.Up(), this._viewMatrix);
  254. return this._viewMatrix;
  255. }
  256. /**
  257. * Returns the projection matrix.
  258. * 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).
  259. * @param viewMatrix The view transform matrix of the light (optional).
  260. * @param renderList The list of meshes to take into account when calculating the projection matrix (optional).
  261. * @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).
  262. */
  263. getProjectionMatrix(viewMatrix, renderList) {
  264. this.setShadowProjectionMatrix(this._projectionMatrix, viewMatrix ?? this._viewMatrix, renderList ?? []);
  265. return this._projectionMatrix;
  266. }
  267. }
  268. __decorate([
  269. serializeAsVector3()
  270. ], ShadowLight.prototype, "position", null);
  271. __decorate([
  272. serializeAsVector3()
  273. ], ShadowLight.prototype, "direction", null);
  274. __decorate([
  275. serialize()
  276. ], ShadowLight.prototype, "shadowMinZ", null);
  277. __decorate([
  278. serialize()
  279. ], ShadowLight.prototype, "shadowMaxZ", null);
  280. //# sourceMappingURL=shadowLight.js.map