Checking if the reticle is horizontal or vertical in webxr

I’m developing with webxr and three.js following the basic setup here.

I need to determine whether the reticle is on a horizontal surface or a vertical one. I assume in the render loop after the line this.reticle.matrix.fromArray(hit.getPose(referenceSpace).transform.matrix);.

What is the best way to do this please?

You can try the method getWorldDirection(v3) on the reticle where v3 is a target vector3 to be passed the world direction, normalising this vector should give you number values to determine if the reticle is facing upwards or sideways

Thank you. I had actually tried that, but the normalize function doesn’t seem to work in this instance. It’s really odd.

Two samples. The first one normalizes perfectly. The second normalize does nothing at all.

Sample 1:

var dir = new THREE.Vector3(-20, 0, 0);
dir.normalize();
console.log(dir);

Output 1: Vector3 {x: -1, y: 0, z: 0}

Sample 2:

var dir = new THREE.Vector3(0, 0, 0);
this.reticle.getWorldDirection(dir);
dir.normalize();
console.log(dir);

Output 2: Vector3 {x: 0.2372211329375215, y: -8.289278695949982e-17, z: 0.9714556778813118}

The dir values in output 2 here are exactly the same as the dir values immediately after the this.reticle.getWorldDirection(dir); command.

It’s like there is something different about the Vector3 returned by getWorldDirection() which makes the normalize() function fail.

OK so I found a solution. I couldn’t get past the getWorldDirection() normalize() issue, but I found a way.

I create an invisible plane within the reticle object, offset in the y direction so that it sits in front of the reticle. This is always in front of the reticle regardless of where the reticle is because it’s a child of the reticle.

    addReticle() {

        let geom;
        if(this.reticleType == 0){
            geom = new THREE.RingGeometry(0.05, 0.07, 32);
        }else if(this.reticleType == 1){
            geom = new THREE.CircleGeometry( .01, 32 );
        }

        this.reticle = new THREE.Mesh(
            geom.rotateX(- Math.PI / 2),
            new THREE.MeshBasicMaterial()
        );
        this.reticle.matrixAutoUpdate = false;
        this.reticle.visible = false;
        this.scene.add(this.reticle);

        let geomLookAt = new THREE.PlaneGeometry(.05, .05);
        this.reticleLookAt = new THREE.Mesh(
            geomLookAt.rotateX(- Math.PI / 2),
            new THREE.MeshBasicMaterial({color: 0xff0000})
        );
        this.reticleLookAt.translateY(.3);
        this.reticleLookAt.visible = false;
        this.reticle.add(this.reticleLookAt);

    }

I can now get the world position of the reticle and reticleLookAt and calculate the subvector between the two to get the direction between them. The y property will contain 1 if facing up, -1 if facing down and anything else if neither of those. So we can calculate floor, wall and ceiling placement with this function:

    getReticleSurface(){
        this.reticleLookAt.getWorldPosition(this.reticleWorldPosition);
        this.reticle.getWorldPosition(this.reticleLookAtWorldPosition);
        this.reticleDirection.subVectors( this.reticleWorldPosition, this.reticleLookAtWorldPosition ).normalize();
        if(this.reticleDirection.y == 1){
            return 'floor';
        }else if(this.reticleDirection.y == -1){
            return 'ceiling';
        }else{
            return 'wall';
        }
    }

this.reticleWorldPosition, this.reticleLookAtWorldPosition, and this.reticleDirection are all defined as class properties so that they are re-used rather than creating a new Vector3() each time.

Hope that helps someone out :slight_smile:

1 Like