reticle.js 6.54 KB
import ARCS from '../engine/arcs.js';
import * as THREE from '../deps/three.js/index.js';

let Reticle;

const createGeometry = function(config) {
    let reticle, geometry;
    config = config ?? {};
    let shape = config.shape ?? 'ring';
    let size = config.size ?? 0.1;
    let color = config.color ?? 'white';
    let helper = config.helper ?? false;
    let position = config.position ?? null;
        
        
    switch(shape) {
        case 'ring': 
            geometry = new THREE.RingGeometry(0.375*size,0.5*size, 32).rotateX(-Math.PI/2);
            break;
        case 'circle':
            geometry = new THREE.CircleGeometry(0.5*size, 32).rotateX(-Math.PI/2);
            break;
        case 'square':
            geometry = new THREE.PlaneGeometry(size, size);//.rotateX(-Math.PI/2);
            break;
        case 'contour':
            const square = new THREE.PlaneGeometry(size, size).rotateX(-Math.PI/2);
            geometry = new THREE.EdgesGeometry(square);
            break;
        case 'sphere':
            geometry = new THREE.SphereGeometry(0.5*size,32,32);
            break;
        case 'cube':
            geometry = new THREE.BoxGeometry(size, size, size).translateY(0.5*size);
            break;
    };
        
    if (!geometry) {
        reticle = new THREE.Object3D();
    } else {
        reticle = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial(
            {color: color, side: THREE.DoubleSide, opacity:0.5, transparent: true}
        ));
    } 
        
    if (helper) {
        reticle.add(new THREE.AxesHelper(size));
    }

    if (position) {
        reticle.position.x = position[0];
        reticle.position.y = position[1];
        reticle.position.z = position[2];
        //reticle.rotation.x = Math.PI / 2;

    }
    return reticle;
};


Reticle = ARCS.Component.create(function(config) {
        let reticle;
        let self = this;
        
        let hitTestSource = null;
        let hitTestSourceRequested = false;
        
        reticle = createGeometry(config);    
        reticle.visible = false;
        reticle.matrixAutoUpdate = false;
    
        this.hide = function() { reticle.visible = false;};
        this.show = function() { reticle.visible = true; };
        this.select = function() {
            console.log("reticle select");
            if (reticle.visible) {
                self.emit('sendMatrix', reticle.matrix);
            }
        };
        
        let rectifyMatrix = function(mat) {
            console.log("position", mat[12], mat[13], mat[14]); 
            const lY = new THREE.Vector3(mat[4],mat[5],mat[6]); 
            const gY = new THREE.Vector3(0,1,0);
            const lZ = gY;
            
            const scal = lY.dot(gY);
            if (scal < 0.01 && scal > -0.01) {
                let lX = new THREE.Vector3();
                lX.crossVectors(lY, lZ);
                /*const newMat =*/ return [
                    lX.x, lX.y, lX.z, 0,
                    lY.x, lY.y, lY.z, 0,
                    lZ.x, lZ.y, lZ.z, 0,
                    mat[12], mat[13], mat[14], 1
                ];                
                //return newMat;
            } else {
                return mat;
            }            
        };
        
        this.update = function(time, camera, frame) {
            if (!frame) return;
            let manager = frame.manager;
            let session = frame.session;
            // in the case hit test has not been called
            if (!frame.session.requestHitTestSource) return;
                                
            if (hitTestSourceRequested === false) {
                frame.session.requestReferenceSpace("viewer").then( refSpace => {
                   frame.session.requestHitTestSource({ space: refSpace }).then(
                       src => hitTestSource = src
                   )                    
                });
                frame.session.addEventListener('end', () => {
                    hitTestSourceRequested = false;
                    hitTestSource = null;
                });
                hitTestSourceRequested = true;
            }
            
            if (hitTestSource) {
                const hitTestResults = frame.getHitTestResults(hitTestSource);
                if (hitTestResults.length) {
                    const hit = hitTestResults[0];                
                    reticle.visible = true;
                    // Orientation obtained from the matrix is completely off.
                    // One can experiment to face the gimbal lock problem.
                    // This is due to the fact the idea is to get a ray, 
                    // especially when in front of a wall
                    // In consequence, pose is rectified in such a case.
                    reticle.matrix.fromArray(
                        rectifyMatrix(
                            hit.getPose(manager.getReferenceSpace()).transform.matrix
                        )
                    );
                } else {
                    reticle.visible = false;
                }
            }
        };
        this.init = function() {
            self.emit('sendScene', reticle);            
        };    
    },
    ['init', 'update', 'select', 'hide', 'show'],
    ['sendScene', 'sendMatrix']
);    

let StaticReticle = ARCS.Component.create(
    function(config) {
        let reticle;
        let self = this;
        reticle = createGeometry(config);        
        reticle.visible = true;
        reticle.matrixAutoUpdate = true;
    
        this.hide = function() { reticle.visible = false;};
        this.show = function() { reticle.visible = true; };
        this.select = function() {
            if (reticle.visible) {
                self.emit('sendMatrix', reticle.matrix);
            }
        };
        this.update = function(time, camera, frame) {
            console.log("static reticle", reticle.position);
        };
        this.init = function() {
            self.emit('sendScene', reticle);            
        };    
 
    },
    ['init', 'update', 'select', 'hide', 'show'],
    ['sendScene', 'sendMatrix']
);


export default {Reticle: Reticle, StaticReticle: StaticReticle};

/*
ARCS.__lib__`
{
    "components": {
        "Reticle": {
            "description": "A reticle component",
            "keywords": ["reticle", "cursor"]
        },
        "StaticReticle": {
            "description": "A static reticle component",
            "keywords": ["reticle", "cursor"]
        }
    },
    "dependencies": [ "three" ],
    "recipes": {
        "from": "node_modules/three/build/three.module.js",
        "to": "deps/three.js/index.js"
    }
}
`;
*/