directionalLightFrustumViewer.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import { StandardMaterial } from "../Materials/standardMaterial.js";
  2. import { Color3 } from "../Maths/math.color.js";
  3. import { Matrix, TmpVectors, Vector3 } from "../Maths/math.vector.js";
  4. import { CreateLines } from "../Meshes/Builders/linesBuilder.js";
  5. import { Mesh } from "../Meshes/mesh.js";
  6. import { VertexData } from "../Meshes/mesh.vertexData.js";
  7. import { TransformNode } from "../Meshes/transformNode.js";
  8. /**
  9. * Class used to render a debug view of the frustum for a directional light
  10. * @see https://playground.babylonjs.com/#7EFGSG#4
  11. * @since 5.0.0
  12. */
  13. export class DirectionalLightFrustumViewer {
  14. /**
  15. * Gets or sets the transparency of the frustum planes
  16. */
  17. get transparency() {
  18. return this._transparency;
  19. }
  20. set transparency(alpha) {
  21. this._transparency = alpha;
  22. for (let i = 6; i < 12; ++i) {
  23. this._lightHelperFrustumMeshes[i].material.alpha = alpha;
  24. }
  25. }
  26. /**
  27. * true to display the edges of the frustum
  28. */
  29. get showLines() {
  30. return this._showLines;
  31. }
  32. set showLines(show) {
  33. if (this._showLines === show) {
  34. return;
  35. }
  36. this._showLines = show;
  37. for (let i = 0; i < 6; ++i) {
  38. this._lightHelperFrustumMeshes[i].setEnabled(show);
  39. }
  40. }
  41. /**
  42. * true to display the planes of the frustum
  43. */
  44. get showPlanes() {
  45. return this._showPlanes;
  46. }
  47. set showPlanes(show) {
  48. if (this._showPlanes === show) {
  49. return;
  50. }
  51. this._showPlanes = show;
  52. for (let i = 6; i < 12; ++i) {
  53. this._lightHelperFrustumMeshes[i].setEnabled(show);
  54. }
  55. }
  56. /**
  57. * Creates a new frustum viewer
  58. * @param light directional light to display the frustum for
  59. * @param camera camera used to retrieve the minZ / maxZ values if the shadowMinZ/shadowMaxZ values of the light are not setup
  60. */
  61. constructor(light, camera) {
  62. this._oldPosition = new Vector3(Number.NaN, Number.NaN, Number.NaN);
  63. this._oldDirection = new Vector3(Number.NaN, Number.NaN, Number.NaN);
  64. this._transparency = 0.3;
  65. this._showLines = true;
  66. this._showPlanes = true;
  67. this._scene = light.getScene();
  68. this._light = light;
  69. this._camera = camera;
  70. this._inverseViewMatrix = Matrix.Identity();
  71. this._lightHelperFrustumMeshes = [];
  72. this._createGeometry();
  73. this.show();
  74. this.update();
  75. }
  76. /**
  77. * Shows the frustum
  78. */
  79. show() {
  80. this._lightHelperFrustumMeshes.forEach((mesh, index) => {
  81. mesh.setEnabled((index < 6 && this._showLines) || (index >= 6 && this._showPlanes));
  82. });
  83. this._oldPosition.set(Number.NaN, Number.NaN, Number.NaN);
  84. this._visible = true;
  85. }
  86. /**
  87. * Hides the frustum
  88. */
  89. hide() {
  90. this._lightHelperFrustumMeshes.forEach((mesh) => {
  91. mesh.setEnabled(false);
  92. });
  93. this._visible = false;
  94. }
  95. /**
  96. * Updates the frustum.
  97. * Call this method to update the frustum view if the light has changed position/direction
  98. */
  99. update() {
  100. if (!this._visible) {
  101. return;
  102. }
  103. if (this._oldPosition.equals(this._light.position) &&
  104. this._oldDirection.equals(this._light.direction) &&
  105. this._oldAutoCalc === this._light.autoCalcShadowZBounds &&
  106. this._oldMinZ === this._light.shadowMinZ &&
  107. this._oldMaxZ === this._light.shadowMaxZ) {
  108. return;
  109. }
  110. this._oldPosition.copyFrom(this._light.position);
  111. this._oldDirection.copyFrom(this._light.direction);
  112. this._oldAutoCalc = this._light.autoCalcShadowZBounds;
  113. this._oldMinZ = this._light.shadowMinZ;
  114. this._oldMaxZ = this._light.shadowMaxZ;
  115. TmpVectors.Vector3[0].set(this._light.orthoLeft, this._light.orthoBottom, this._light.shadowMinZ !== undefined ? this._light.shadowMinZ : this._camera.minZ); // min light extents
  116. TmpVectors.Vector3[1].set(this._light.orthoRight, this._light.orthoTop, this._light.shadowMaxZ !== undefined ? this._light.shadowMaxZ : this._camera.maxZ); // max light extents
  117. const invLightView = this._getInvertViewMatrix();
  118. TmpVectors.Vector3[2].copyFromFloats(TmpVectors.Vector3[1].x, TmpVectors.Vector3[1].y, TmpVectors.Vector3[0].z); // n1
  119. TmpVectors.Vector3[3].copyFromFloats(TmpVectors.Vector3[1].x, TmpVectors.Vector3[0].y, TmpVectors.Vector3[0].z); // n2
  120. TmpVectors.Vector3[4].copyFromFloats(TmpVectors.Vector3[0].x, TmpVectors.Vector3[0].y, TmpVectors.Vector3[0].z); // n3
  121. TmpVectors.Vector3[5].copyFromFloats(TmpVectors.Vector3[0].x, TmpVectors.Vector3[1].y, TmpVectors.Vector3[0].z); // n4
  122. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[2], invLightView, TmpVectors.Vector3[2]); // near1
  123. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[3], invLightView, TmpVectors.Vector3[3]); // near2
  124. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[4], invLightView, TmpVectors.Vector3[4]); // near3
  125. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[5], invLightView, TmpVectors.Vector3[5]); // near4
  126. TmpVectors.Vector3[6].copyFromFloats(TmpVectors.Vector3[1].x, TmpVectors.Vector3[1].y, TmpVectors.Vector3[1].z); // f1
  127. TmpVectors.Vector3[7].copyFromFloats(TmpVectors.Vector3[1].x, TmpVectors.Vector3[0].y, TmpVectors.Vector3[1].z); // f2
  128. TmpVectors.Vector3[8].copyFromFloats(TmpVectors.Vector3[0].x, TmpVectors.Vector3[0].y, TmpVectors.Vector3[1].z); // f3
  129. TmpVectors.Vector3[9].copyFromFloats(TmpVectors.Vector3[0].x, TmpVectors.Vector3[1].y, TmpVectors.Vector3[1].z); // f4
  130. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[6], invLightView, TmpVectors.Vector3[6]); // far1
  131. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[7], invLightView, TmpVectors.Vector3[7]); // far2
  132. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[8], invLightView, TmpVectors.Vector3[8]); // far3
  133. Vector3.TransformCoordinatesToRef(TmpVectors.Vector3[9], invLightView, TmpVectors.Vector3[9]); // far4
  134. CreateLines("nearlines", { updatable: true, points: this._nearLinesPoints, instance: this._lightHelperFrustumMeshes[0] }, this._scene);
  135. CreateLines("farlines", { updatable: true, points: this._farLinesPoints, instance: this._lightHelperFrustumMeshes[1] }, this._scene);
  136. CreateLines("trlines", { updatable: true, points: this._trLinesPoints, instance: this._lightHelperFrustumMeshes[2] }, this._scene);
  137. CreateLines("brlines", { updatable: true, points: this._brLinesPoints, instance: this._lightHelperFrustumMeshes[3] }, this._scene);
  138. CreateLines("tllines", { updatable: true, points: this._tlLinesPoints, instance: this._lightHelperFrustumMeshes[4] }, this._scene);
  139. CreateLines("bllines", { updatable: true, points: this._blLinesPoints, instance: this._lightHelperFrustumMeshes[5] }, this._scene);
  140. TmpVectors.Vector3[2].toArray(this._nearPlaneVertices, 0);
  141. TmpVectors.Vector3[3].toArray(this._nearPlaneVertices, 3);
  142. TmpVectors.Vector3[4].toArray(this._nearPlaneVertices, 6);
  143. TmpVectors.Vector3[5].toArray(this._nearPlaneVertices, 9);
  144. this._lightHelperFrustumMeshes[6].geometry?.updateVerticesDataDirectly("position", this._nearPlaneVertices, 0);
  145. TmpVectors.Vector3[6].toArray(this._farPlaneVertices, 0);
  146. TmpVectors.Vector3[7].toArray(this._farPlaneVertices, 3);
  147. TmpVectors.Vector3[8].toArray(this._farPlaneVertices, 6);
  148. TmpVectors.Vector3[9].toArray(this._farPlaneVertices, 9);
  149. this._lightHelperFrustumMeshes[7].geometry?.updateVerticesDataDirectly("position", this._farPlaneVertices, 0);
  150. TmpVectors.Vector3[2].toArray(this._rightPlaneVertices, 0);
  151. TmpVectors.Vector3[6].toArray(this._rightPlaneVertices, 3);
  152. TmpVectors.Vector3[7].toArray(this._rightPlaneVertices, 6);
  153. TmpVectors.Vector3[3].toArray(this._rightPlaneVertices, 9);
  154. this._lightHelperFrustumMeshes[8].geometry?.updateVerticesDataDirectly("position", this._rightPlaneVertices, 0);
  155. TmpVectors.Vector3[5].toArray(this._leftPlaneVertices, 0);
  156. TmpVectors.Vector3[9].toArray(this._leftPlaneVertices, 3);
  157. TmpVectors.Vector3[8].toArray(this._leftPlaneVertices, 6);
  158. TmpVectors.Vector3[4].toArray(this._leftPlaneVertices, 9);
  159. this._lightHelperFrustumMeshes[9].geometry?.updateVerticesDataDirectly("position", this._leftPlaneVertices, 0);
  160. TmpVectors.Vector3[2].toArray(this._topPlaneVertices, 0);
  161. TmpVectors.Vector3[6].toArray(this._topPlaneVertices, 3);
  162. TmpVectors.Vector3[9].toArray(this._topPlaneVertices, 6);
  163. TmpVectors.Vector3[5].toArray(this._topPlaneVertices, 9);
  164. this._lightHelperFrustumMeshes[10].geometry?.updateVerticesDataDirectly("position", this._topPlaneVertices, 0);
  165. TmpVectors.Vector3[3].toArray(this._bottomPlaneVertices, 0);
  166. TmpVectors.Vector3[7].toArray(this._bottomPlaneVertices, 3);
  167. TmpVectors.Vector3[8].toArray(this._bottomPlaneVertices, 6);
  168. TmpVectors.Vector3[4].toArray(this._bottomPlaneVertices, 9);
  169. this._lightHelperFrustumMeshes[11].geometry?.updateVerticesDataDirectly("position", this._bottomPlaneVertices, 0);
  170. }
  171. /**
  172. * Dispose of the class / remove the frustum view
  173. */
  174. dispose() {
  175. this._lightHelperFrustumMeshes.forEach((mesh) => {
  176. mesh.material?.dispose();
  177. mesh.dispose();
  178. });
  179. this._rootNode.dispose();
  180. }
  181. _createGeometry() {
  182. this._rootNode = new TransformNode("directionalLightHelperRoot_" + this._light.name, this._scene);
  183. this._rootNode.parent = this._light.parent;
  184. this._nearLinesPoints = [TmpVectors.Vector3[0], TmpVectors.Vector3[1], TmpVectors.Vector3[2], TmpVectors.Vector3[3], TmpVectors.Vector3[4]];
  185. const nearLines = CreateLines("nearlines", { updatable: true, points: this._nearLinesPoints }, this._scene);
  186. nearLines.parent = this._rootNode;
  187. nearLines.alwaysSelectAsActiveMesh = true;
  188. this._farLinesPoints = [TmpVectors.Vector3[5], TmpVectors.Vector3[6], TmpVectors.Vector3[7], TmpVectors.Vector3[8], TmpVectors.Vector3[9]];
  189. const farLines = CreateLines("farlines", { updatable: true, points: this._farLinesPoints }, this._scene);
  190. farLines.parent = this._rootNode;
  191. farLines.alwaysSelectAsActiveMesh = true;
  192. this._trLinesPoints = [TmpVectors.Vector3[10], TmpVectors.Vector3[11]];
  193. const trLines = CreateLines("trlines", { updatable: true, points: this._trLinesPoints }, this._scene);
  194. trLines.parent = this._rootNode;
  195. trLines.alwaysSelectAsActiveMesh = true;
  196. this._brLinesPoints = [TmpVectors.Vector3[12], TmpVectors.Vector3[0]];
  197. const brLines = CreateLines("brlines", { updatable: true, points: this._brLinesPoints }, this._scene);
  198. brLines.parent = this._rootNode;
  199. brLines.alwaysSelectAsActiveMesh = true;
  200. this._tlLinesPoints = [TmpVectors.Vector3[1], TmpVectors.Vector3[2]];
  201. const tlLines = CreateLines("tllines", { updatable: true, points: this._tlLinesPoints }, this._scene);
  202. tlLines.parent = this._rootNode;
  203. tlLines.alwaysSelectAsActiveMesh = true;
  204. this._blLinesPoints = [TmpVectors.Vector3[3], TmpVectors.Vector3[4]];
  205. const blLines = CreateLines("bllines", { updatable: true, points: this._blLinesPoints }, this._scene);
  206. blLines.parent = this._rootNode;
  207. blLines.alwaysSelectAsActiveMesh = true;
  208. this._lightHelperFrustumMeshes.push(nearLines, farLines, trLines, brLines, tlLines, blLines);
  209. const makePlane = (name, color, positions) => {
  210. const plane = new Mesh(name + "plane", this._scene);
  211. const mat = new StandardMaterial(name + "PlaneMat", this._scene);
  212. plane.material = mat;
  213. plane.parent = this._rootNode;
  214. plane.alwaysSelectAsActiveMesh = true;
  215. mat.emissiveColor = color;
  216. mat.alpha = this.transparency;
  217. mat.backFaceCulling = false;
  218. mat.disableLighting = true;
  219. const indices = [0, 1, 2, 0, 2, 3];
  220. const vertexData = new VertexData();
  221. vertexData.positions = positions;
  222. vertexData.indices = indices;
  223. vertexData.applyToMesh(plane, true);
  224. this._lightHelperFrustumMeshes.push(plane);
  225. };
  226. this._nearPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  227. this._farPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  228. this._rightPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  229. this._leftPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  230. this._topPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  231. this._bottomPlaneVertices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  232. makePlane("near", new Color3(1, 0, 0), this._nearPlaneVertices);
  233. makePlane("far", new Color3(0.3, 0, 0), this._farPlaneVertices);
  234. makePlane("right", new Color3(0, 1, 0), this._rightPlaneVertices);
  235. makePlane("left", new Color3(0, 0.3, 0), this._leftPlaneVertices);
  236. makePlane("top", new Color3(0, 0, 1), this._topPlaneVertices);
  237. makePlane("bottom", new Color3(0, 0, 0.3), this._bottomPlaneVertices);
  238. this._nearLinesPoints[0] = TmpVectors.Vector3[2];
  239. this._nearLinesPoints[1] = TmpVectors.Vector3[3];
  240. this._nearLinesPoints[2] = TmpVectors.Vector3[4];
  241. this._nearLinesPoints[3] = TmpVectors.Vector3[5];
  242. this._nearLinesPoints[4] = TmpVectors.Vector3[2];
  243. this._farLinesPoints[0] = TmpVectors.Vector3[6];
  244. this._farLinesPoints[1] = TmpVectors.Vector3[7];
  245. this._farLinesPoints[2] = TmpVectors.Vector3[8];
  246. this._farLinesPoints[3] = TmpVectors.Vector3[9];
  247. this._farLinesPoints[4] = TmpVectors.Vector3[6];
  248. this._trLinesPoints[0] = TmpVectors.Vector3[2];
  249. this._trLinesPoints[1] = TmpVectors.Vector3[6];
  250. this._brLinesPoints[0] = TmpVectors.Vector3[3];
  251. this._brLinesPoints[1] = TmpVectors.Vector3[7];
  252. this._tlLinesPoints[0] = TmpVectors.Vector3[4];
  253. this._tlLinesPoints[1] = TmpVectors.Vector3[8];
  254. this._blLinesPoints[0] = TmpVectors.Vector3[5];
  255. this._blLinesPoints[1] = TmpVectors.Vector3[9];
  256. }
  257. _getInvertViewMatrix() {
  258. Matrix.LookAtLHToRef(this._light.position, this._light.position.add(this._light.direction), Vector3.UpReadOnly, this._inverseViewMatrix);
  259. this._inverseViewMatrix.invertToRef(this._inverseViewMatrix);
  260. return this._inverseViewMatrix;
  261. }
  262. }
  263. //# sourceMappingURL=directionalLightFrustumViewer.js.map