rayHelper.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import { Nullable } from "../types";
  2. import { Ray } from "../Culling/ray";
  3. import { Vector3 } from "../Maths/math.vector";
  4. import { Color3 } from '../Maths/math.color';
  5. import { Scene } from "../scene";
  6. import { AbstractMesh } from "../Meshes/abstractMesh";
  7. import { Mesh } from "../Meshes/mesh";
  8. import { LinesMesh } from "../Meshes/linesMesh";
  9. import "../Meshes/Builders/linesBuilder";
  10. import { Observer } from '../Misc/observable';
  11. /**
  12. * As raycast might be hard to debug, the RayHelper can help rendering the different rays
  13. * in order to better appreciate the issue one might have.
  14. * @see https://doc.babylonjs.com/babylon101/raycasts#debugging
  15. */
  16. export class RayHelper {
  17. /**
  18. * Defines the ray we are currently tryin to visualize.
  19. */
  20. public ray: Nullable<Ray>;
  21. private _renderPoints: Vector3[];
  22. private _renderLine: Nullable<LinesMesh>;
  23. private _renderFunction: Nullable<() => void>;
  24. private _scene: Nullable<Scene>;
  25. private _onAfterRenderObserver: Nullable<Observer<Scene>>;
  26. private _onAfterStepObserver: Nullable<Observer<Scene>>;
  27. private _attachedToMesh: Nullable<AbstractMesh>;
  28. private _meshSpaceDirection: Vector3;
  29. private _meshSpaceOrigin: Vector3;
  30. /**
  31. * Helper function to create a colored helper in a scene in one line.
  32. * @param ray Defines the ray we are currently tryin to visualize
  33. * @param scene Defines the scene the ray is used in
  34. * @param color Defines the color we want to see the ray in
  35. * @returns The newly created ray helper.
  36. */
  37. public static CreateAndShow(ray: Ray, scene: Scene, color: Color3): RayHelper {
  38. var helper = new RayHelper(ray);
  39. helper.show(scene, color);
  40. return helper;
  41. }
  42. /**
  43. * Instantiate a new ray helper.
  44. * As raycast might be hard to debug, the RayHelper can help rendering the different rays
  45. * in order to better appreciate the issue one might have.
  46. * @see https://doc.babylonjs.com/babylon101/raycasts#debugging
  47. * @param ray Defines the ray we are currently tryin to visualize
  48. */
  49. constructor(ray: Ray) {
  50. this.ray = ray;
  51. }
  52. /**
  53. * Shows the ray we are willing to debug.
  54. * @param scene Defines the scene the ray needs to be rendered in
  55. * @param color Defines the color the ray needs to be rendered in
  56. */
  57. public show(scene: Scene, color?: Color3): void {
  58. if (!this._renderFunction && this.ray) {
  59. var ray = this.ray;
  60. this._renderFunction = this._render.bind(this);
  61. this._scene = scene;
  62. this._renderPoints = [ray.origin, ray.origin.add(ray.direction.scale(ray.length))];
  63. this._renderLine = Mesh.CreateLines("ray", this._renderPoints, scene, true);
  64. this._renderLine.isPickable = false;
  65. if (this._renderFunction) {
  66. this._scene.registerBeforeRender(this._renderFunction);
  67. }
  68. }
  69. if (color && this._renderLine) {
  70. this._renderLine.color.copyFrom(color);
  71. }
  72. }
  73. /**
  74. * Hides the ray we are debugging.
  75. */
  76. public hide(): void {
  77. if (this._renderFunction && this._scene) {
  78. this._scene.unregisterBeforeRender(this._renderFunction);
  79. this._scene = null;
  80. this._renderFunction = null;
  81. if (this._renderLine) {
  82. this._renderLine.dispose();
  83. this._renderLine = null;
  84. }
  85. this._renderPoints = [];
  86. }
  87. }
  88. private _render(): void {
  89. var ray = this.ray;
  90. if (!ray) {
  91. return;
  92. }
  93. var point = this._renderPoints[1];
  94. var len = Math.min(ray.length, 1000000);
  95. point.copyFrom(ray.direction);
  96. point.scaleInPlace(len);
  97. point.addInPlace(ray.origin);
  98. Mesh.CreateLines("ray", this._renderPoints, this._scene, true, this._renderLine);
  99. }
  100. /**
  101. * Attach a ray helper to a mesh so that we can easily see its orientation for instance or information like its normals.
  102. * @param mesh Defines the mesh we want the helper attached to
  103. * @param meshSpaceDirection Defines the direction of the Ray in mesh space (local space of the mesh node)
  104. * @param meshSpaceOrigin Defines the origin of the Ray in mesh space (local space of the mesh node)
  105. * @param length Defines the length of the ray
  106. */
  107. public attachToMesh(mesh: AbstractMesh, meshSpaceDirection?: Vector3, meshSpaceOrigin?: Vector3, length?: number): void {
  108. this._attachedToMesh = mesh;
  109. var ray = this.ray;
  110. if (!ray) {
  111. return;
  112. }
  113. if (!ray.direction) {
  114. ray.direction = Vector3.Zero();
  115. }
  116. if (!ray.origin) {
  117. ray.origin = Vector3.Zero();
  118. }
  119. if (length) {
  120. ray.length = length;
  121. }
  122. if (!meshSpaceOrigin) {
  123. meshSpaceOrigin = Vector3.Zero();
  124. }
  125. if (!meshSpaceDirection) {
  126. // -1 so that this will work with Mesh.lookAt
  127. meshSpaceDirection = new Vector3(0, 0, -1);
  128. }
  129. if (!this._scene) {
  130. this._scene = mesh.getScene();
  131. }
  132. if (!this._meshSpaceDirection) {
  133. this._meshSpaceDirection = meshSpaceDirection.clone();
  134. this._meshSpaceOrigin = meshSpaceOrigin.clone();
  135. } else {
  136. this._meshSpaceDirection.copyFrom(meshSpaceDirection);
  137. this._meshSpaceOrigin.copyFrom(meshSpaceOrigin);
  138. }
  139. if (!this._onAfterRenderObserver) {
  140. this._onAfterRenderObserver = this._scene.onBeforeRenderObservable.add(() => this._updateToMesh());
  141. this._onAfterStepObserver = this._scene.onAfterStepObservable.add(() => this._updateToMesh());
  142. }
  143. // force world matrix computation before the first ray helper computation
  144. this._attachedToMesh.computeWorldMatrix(true);
  145. this._updateToMesh();
  146. }
  147. /**
  148. * Detach the ray helper from the mesh it has previously been attached to.
  149. */
  150. public detachFromMesh(): void {
  151. if (this._attachedToMesh && this._scene) {
  152. if (this._onAfterRenderObserver) {
  153. this._scene.onBeforeRenderObservable.remove(this._onAfterRenderObserver);
  154. this._scene.onAfterStepObservable.remove(this._onAfterStepObserver);
  155. }
  156. this._attachedToMesh = null;
  157. this._onAfterRenderObserver = null;
  158. this._onAfterStepObserver = null;
  159. this._scene = null;
  160. }
  161. }
  162. private _updateToMesh(): void {
  163. var ray = this.ray;
  164. if (!this._attachedToMesh || !ray) {
  165. return;
  166. }
  167. if (this._attachedToMesh._isDisposed) {
  168. this.detachFromMesh();
  169. return;
  170. }
  171. this._attachedToMesh.getDirectionToRef(this._meshSpaceDirection, ray.direction);
  172. Vector3.TransformCoordinatesToRef(this._meshSpaceOrigin, this._attachedToMesh.getWorldMatrix(), ray.origin);
  173. }
  174. /**
  175. * Dispose the helper and release its associated resources.
  176. */
  177. public dispose(): void {
  178. this.hide();
  179. this.detachFromMesh();
  180. this.ray = null;
  181. }
  182. }