Project 3D point on plane

I need to project a 3D point on a given plane - as of right now I am relying on scene.multiPickWithRay - however, I am under the impression that there has to be a better way. Anybody can point me to the right method implementation in Babylon?

getRayHitPointOnPlane(point) {                
        const length = 10;

        let origin = camera.position;
        let direction = point.subtract(origin).normalize();

        let ray = new BABYLON.Ray(origin, direction, length);
        var hits = scene.multiPickWithRay(ray);

        let pc = new BABYLON.Vector3(0, 0, 0);

        if (hits) {
            for (var i = 0; i < hits.length; i++) {
                let hit = hits[i];
                if (hit.pickedMesh.name == "apf-" + label.name) {
                    pc = hit.pickedPoint;                    
                }
            }
        } else {
            console.log("No hits");
        }

        return pc;
    }

Well honestly I would have done the same but with scene.pickWithRay

I have multiple planes and therefore use multi pick to make sure it projects on the plane I want.

so all good!

This approach is very slow however. I need to project a lot of points. Any other method that handles this?

Well you need to unproject no matter what. Then you can manually do the picking just using the bounding info of your plan?

here is the relevant code to repro:

(see the onlyBoundingInfo param)

I don’t follow. I am not sure what you mean by “using bounding info of your plan” - while I see your fastCheck flag and onlyBoundingInfo, I don’t really follow how to translate this to the code snippet above.

this is the code of multipick:

as you can see it uses internalMultiPick. So my point was that you may want to rewrite it based on the code I shared

But honestly not sure it will be faster as you still go through layers of code used to compute the intersection.

If you want to project a point onto a plane, you may simply want to do a mathematical projection: Projection of a point onto a plane, Projection of a point onto a line

Maybe people on the forum will be happy to add the function for you on the framework (cc @jerome, @JohnK)

@Deltakosh: The link you provided for mathematical projection provides an orthogonal projection - we are interested here in mathematically projecting along a given ray - i.e. the mathematical (and faster) way of getting the intersection point between a given ray (line) and plane. I have a simple solution that yields 20x improvements for me. Maybe the supporters on the forum can include it in the framework eventually.

For others that may have this issue:

If you have a mesh plane, make sure you use a source plane when creating it:

// apf position is the position of the plane
// The plane normal is the camera's forward ray normalized and negated
var sourcePlane = BABYLON.Plane.FromPositionAndNormal(apfPosition, camera.getForwardRay().direction.normalize().negate());
var plane = BABYLON.MeshBuilder.CreatePlane("apf-" + label.name, { width: this._width, height: this._height, sourcePlane: sourcePlane}, scene);

Now the function, that relies on the following slide: Ray-Plane Intersection

/**    
     * Projects a point to a plane along a ray starting from the camera origin and directed towards the point. 
     * @param {BABYLON.Vector3} point      
     * @param {BABYLON.Plane} plane
     * @return {BABYLON.Vector3} The projection of the point p on the plane
     */
    projectOnPlane(point, plane) {        
        let n = plane.normal;        
        let d = plane.d;

        
        // ray origin
        let p0 = camera.position;
        // ray direction
        let V = point.subtract(p0).normalize();

        let denom = BABYLON.Vector3.Dot(V, n);
        let t = -(BABYLON.Vector3.Dot(p0, n) + d)/denom;

        // P = P0 + t*V
        return p0.add(V.scale(t));
    }
2 Likes

Do you want to add that function to the Vector3 class?

Well I will add it for you (as you provided all the code :))

here is what you will have in the new nightly (it will be even faster as I will remove all GC call):

    /**    
     * Projects a point to a plane along a ray starting from a specified origin and directed towards the point. 
     * @param origin defines the origin of the projection ray
     * @param plane defines the plane to project to
     * @param result defines the Vector3 where to store the result
     */
    public projectOnPlaneToRef(plane: Plane, origin: Vector3, result: Vector3): void {  

Perfect - so including

<script src="https://preview.babylonjs.com/babylon.js"></script>

means that tomorrow morning I should have that method in the Vector3 class?

correct :smiley: