[SOLVED] WebXR HitTest: How to hit test and show a reticle on the floor?

I’m looking to replicate this reticule behavior from the Immersive-web hit-test demo.

The only way I knew how to do Hit-Test with Playcanvas is when a new inputSource (or screen tap) is detected, like Moka did in this sample project.

 // in AR touch on screen adds new input source every time
    this.app.xr.input.on('add', function(inputSource) {
        
        // start hit test for added input source
        inputSource.hitTestStart({
            callback: function(err, hitTestSource) {
                // if result is reported
                hitTestSource.on('result', function(position, rotation) {
                    ...
                });
            }
        });
    });

But, I want to hit-test in the update function while moving my mobile around to show a reticle where the floor is detected.

In the immersive-web demo, they can access to an hitTestSource from the XrSession like this:

// In this sample we want to cast a ray straight out from the viewer's
// position and render a reticle where it intersects with a real world
// surface. To do this we first get the viewer space, then create a
// hitTestSource that tracks it.
session.requestReferenceSpace('viewer').then((refSpace) => {
   xrViewerSpace = refSpace;
   session.requestHitTestSource({ space: xrViewerSpace }).then((hitTestSource) => {
        xrHitTestSource = hitTestSource;
   });
});

And hit-test every frame to position the reticule:

// Called every time a XRSession requests that a new frame be drawn.
      function onXRFrame(t, frame) {
        let session = frame.session;
        let pose = frame.getViewerPose(xrRefSpace);

        reticle.visible = false;

        // If we have a hit test source, get its results for the frame
        // and use the pose to display a reticle in the scene.
        if (xrHitTestSource && pose) {
          let hitTestResults = frame.getHitTestResults(xrHitTestSource);
          if (hitTestResults.length > 0) {
            let pose = hitTestResults[0].getPose(xrRefSpace);
            reticle.visible = true;
            reticle.matrix = pose.transform.matrix;
          }
        }

        [...]
      }

Is this possible to do that with the current state of implementation on WebXR in Playcanvas?

By analyzing this script in the engine in GitHub, I found an example doing this, which is doing what I want.

HitTest.prototype.update = function(dt) {   
    this.app.xr.hitTest.start({
        spaceType: pc.XRSPACE_VIEWER,
        callback: function (err, hitTestSource) {
            
            if (err) return;
            hitTestSource.on('result', function (position, rotation) {
                
            });
        }
    });
    
};

The only thing is that’s probably heavy and slow. By adding this, I can see less fluidity when moving and rotating my mobile around.

If you find a better solution, please post it here.

1 Like

Haven’t tried this yet but it looks like you only need to start the hitTest once and listen for the result event to update the reticle position and rotation: https://github.com/playcanvas/playcanvas.github.io/blob/master/xr/ar-hit-test.html

1 Like

Oh yeah! It’s true!

So, actually, we can do something similar to this:

app.xr.on('start', function () {
                    message("Immersive AR session has started");

                    if (! app.xr.hitTest.supported)
                        return;

                    app.xr.hitTest.start({
                        entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
                        callback: function (err, hitTestSource) {
                            if (err) {
                                message("Failed to start AR hit test");
                                return;
                            }

                            hitTestSource.on('result', function (position, rotation) {
                                target.setPosition(position);
                                target.setRotation(rotation);
                            });
                        }
                    });
                });

I’m not sure how to ‘end’ the hit test source though. It seems odd that you can start one but not change it or ‘end’ it :thinking:

Edit: Ah, it’s https://github.com/playcanvas/engine/blob/a85aeed65d2bddef1646929105caba84d1a8f912/src/xr/xr-hit-test-source.js#L62

1 Like

Thank you for the information :slight_smile: