import { Vector3 } from "../Maths/math.vector.js"; import { CreateLines } from "../Meshes/Builders/linesBuilder.js"; /** * As raycast might be hard to debug, the RayHelper can help rendering the different rays * in order to better appreciate the issue one might have. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/interactions/picking_collisions#debugging */ export class RayHelper { /** * Helper function to create a colored helper in a scene in one line. * @param ray Defines the ray we are currently trying to visualize * @param scene Defines the scene the ray is used in * @param color Defines the color we want to see the ray in * @returns The newly created ray helper. */ static CreateAndShow(ray, scene, color) { const helper = new RayHelper(ray); helper.show(scene, color); return helper; } /** * Instantiate a new ray helper. * As raycast might be hard to debug, the RayHelper can help rendering the different rays * in order to better appreciate the issue one might have. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/interactions/picking_collisions#debugging * @param ray Defines the ray we are currently trying to visualize */ constructor(ray) { this.ray = ray; } /** * Shows the ray we are willing to debug. * @param scene Defines the scene the ray needs to be rendered in * @param color Defines the color the ray needs to be rendered in */ show(scene, color) { if (!this._renderFunction && this.ray) { const ray = this.ray; this._renderFunction = () => this._render(); this._scene = scene; this._renderPoints = [ray.origin, ray.origin.add(ray.direction.scale(ray.length))]; this._renderLine = CreateLines("ray", { points: this._renderPoints, updatable: true }, scene); this._renderLine.isPickable = false; if (this._renderFunction) { this._scene.registerBeforeRender(this._renderFunction); } } if (color && this._renderLine) { this._renderLine.color.copyFrom(color); } } /** * Hides the ray we are debugging. */ hide() { if (this._renderFunction && this._scene) { this._scene.unregisterBeforeRender(this._renderFunction); this._scene = null; this._renderFunction = null; if (this._renderLine) { this._renderLine.dispose(); this._renderLine = null; } this._renderPoints = []; } } _render() { const ray = this.ray; if (!ray) { return; } const point = this._renderPoints[1]; const len = Math.min(ray.length, 1000000); point.copyFrom(ray.direction); point.scaleInPlace(len); point.addInPlace(ray.origin); this._renderPoints[0].copyFrom(ray.origin); CreateLines("ray", { points: this._renderPoints, updatable: true, instance: this._renderLine }, this._scene); this._renderLine?.refreshBoundingInfo(); } /** * Attach a ray helper to a mesh so that we can easily see its orientation for instance or information like its normals. * @param mesh Defines the mesh we want the helper attached to * @param meshSpaceDirection Defines the direction of the Ray in mesh space (local space of the mesh node) * @param meshSpaceOrigin Defines the origin of the Ray in mesh space (local space of the mesh node) * @param length Defines the length of the ray */ attachToMesh(mesh, meshSpaceDirection, meshSpaceOrigin, length) { this._attachedToMesh = mesh; const ray = this.ray; if (!ray) { return; } if (!ray.direction) { ray.direction = Vector3.Zero(); } if (!ray.origin) { ray.origin = Vector3.Zero(); } if (length) { ray.length = length; } if (!meshSpaceOrigin) { meshSpaceOrigin = Vector3.Zero(); } if (!meshSpaceDirection) { // -1 so that this will work with Mesh.lookAt meshSpaceDirection = new Vector3(0, 0, -1); } if (!this._scene) { this._scene = mesh.getScene(); } if (!this._meshSpaceDirection) { this._meshSpaceDirection = meshSpaceDirection.clone(); this._meshSpaceOrigin = meshSpaceOrigin.clone(); } else { this._meshSpaceDirection.copyFrom(meshSpaceDirection); this._meshSpaceOrigin.copyFrom(meshSpaceOrigin); } if (!this._onAfterRenderObserver) { this._onAfterRenderObserver = this._scene.onBeforeRenderObservable.add(() => this._updateToMesh()); this._onAfterStepObserver = this._scene.onAfterStepObservable.add(() => this._updateToMesh()); } // force world matrix computation before the first ray helper computation this._attachedToMesh.computeWorldMatrix(true); this._updateToMesh(); } /** * Detach the ray helper from the mesh it has previously been attached to. */ detachFromMesh() { if (this._attachedToMesh && this._scene) { if (this._onAfterRenderObserver) { this._scene.onBeforeRenderObservable.remove(this._onAfterRenderObserver); this._scene.onAfterStepObservable.remove(this._onAfterStepObserver); } this._attachedToMesh = null; this._onAfterRenderObserver = null; this._onAfterStepObserver = null; this._scene = null; } } _updateToMesh() { const ray = this.ray; if (!this._attachedToMesh || !ray) { return; } if (this._attachedToMesh.isDisposed()) { this.detachFromMesh(); return; } this._attachedToMesh.getDirectionToRef(this._meshSpaceDirection, ray.direction); Vector3.TransformCoordinatesToRef(this._meshSpaceOrigin, this._attachedToMesh.getWorldMatrix(), ray.origin); } /** * Dispose the helper and release its associated resources. */ dispose() { this.hide(); this.detachFromMesh(); this.ray = null; } } //# sourceMappingURL=rayHelper.js.map