Files
AR-Menu/AR Menu/Needle/MenuScene/node_modules/three-mesh-ui/examples/controls/VRControl.js
2025-11-30 08:35:03 +02:00

213 lines
4.8 KiB
JavaScript

/*
Job: creating the VR controllers and their pointers
*/
import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory.js';
import { BoxGeometry, CanvasTexture, Matrix4, Mesh, MeshBasicMaterial, Sprite, SpriteMaterial } from 'three';
export default function VRControl( renderer ) {
const controllers = [];
const controllerGrips = [];
const controllerModelFactory = new XRControllerModelFactory();
//////////////////
// Lines helpers
//////////////////
const material = new MeshBasicMaterial( {
color: 0xffffff,
alphaMap: new CanvasTexture( generateRayTexture() ),
transparent: true
});
const geometry = new BoxGeometry( 0.004, 0.004, 0.35 );
geometry.translate( 0, 0, -0.15 );
const uvAttribute = geometry.attributes.uv;
// @TODO: This could be simplified and optimized
for ( let i = 0; i < uvAttribute.count; i ++ ) {
let u = uvAttribute.getX( i );
let v = uvAttribute.getY( i );
[ u, v ] = (()=> {
switch ( i ) {
case 0 : return [ 1, 1 ]
case 1 : return [ 0, 0 ]
case 2 : return [ 1, 1 ]
case 3 : return [ 0, 0 ]
case 4 : return [ 0, 0 ]
case 5 : return [ 1, 1 ]
case 6 : return [ 0, 0 ]
case 7 : return [ 1, 1 ]
case 8 : return [ 0, 0 ]
case 9 : return [ 0, 0 ]
case 10 : return [ 1, 1 ]
case 11 : return [ 1, 1 ]
case 12 : return [ 1, 1 ]
case 13 : return [ 1, 1 ]
case 14 : return [ 0, 0 ]
case 15 : return [ 0, 0 ]
default : return [ 0, 0 ]
}
})();
uvAttribute.setXY( i, u, v );
}
const linesHelper = new Mesh( geometry, material );
linesHelper.renderOrder = Infinity;
/////////////////
// Point helper
/////////////////
const spriteMaterial = new SpriteMaterial({
map: new CanvasTexture( generatePointerTexture() ),
sizeAttenuation: false,
depthTest: false
});
const pointer = new Sprite( spriteMaterial );
pointer.scale.set(0.015, 0.015, 1)
pointer.renderOrder = Infinity;
////////////////
// Controllers
////////////////
const controller1 = renderer.xr.getController( 0 );
const controller2 = renderer.xr.getController( 1 );
controller1.name = "controller-right";
controller2.name = "controller-left";
const controllerGrip1 = renderer.xr.getControllerGrip( 0 );
const controllerGrip2 = renderer.xr.getControllerGrip( 1 );
if ( controller1 ) controllers.push( controller1 );
if ( controller2 ) controllers.push( controller2 );
if ( controllerGrip1 ) controllerGrips.push( controllerGrip1 );
if ( controllerGrip2 ) controllerGrips.push( controllerGrip2 );
controllers.forEach( (controller)=> {
const ray = linesHelper.clone();
const point = pointer.clone();
controller.add( ray, point );
controller.ray = ray;
controller.point = point;
});
controllerGrips.forEach( (controllerGrip)=> {
controllerGrip.add( controllerModelFactory.createControllerModel( controllerGrip ) );
});
//////////////
// Functions
//////////////
const dummyMatrix = new Matrix4();
// Set the passed ray to match the given controller pointing direction
function setFromController( controllerID, ray ) {
const controller = controllers[ controllerID ];
// Position the intersection ray
dummyMatrix.identity().extractRotation( controller.matrixWorld );
ray.origin.setFromMatrixPosition( controller.matrixWorld );
ray.direction.set( 0, 0, - 1 ).applyMatrix4( dummyMatrix );
}
// Position the chosen controller's pointer at the given point in space.
// Should be called after raycaster.intersectObject() found an intersection point.
function setPointerAt( controllerID, vec ) {
const controller = controllers[ controllerID ];
const localVec = controller.worldToLocal( vec );
controller.point.position.copy( localVec );
controller.point.visible = true;
}
//
return {
controllers,
controllerGrips,
setFromController,
setPointerAt
};
}
//////////////////////////////
// CANVAS TEXTURE GENERATION
//////////////////////////////
// Generate the texture needed to make the intersection ray fade away
/**
*
* @returns {HTMLCanvasElement}
*/
function generateRayTexture() {
const canvas = document.createElement( 'canvas' );
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext("2d");
const gradient = ctx.createLinearGradient(0, 0, 64, 0);
gradient.addColorStop(0, "black");
gradient.addColorStop(1, "white");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 64, 64);
return canvas;
}
// Generate the texture of the point helper sprite
/**
*
* @returns {HTMLCanvasElement}
*/
function generatePointerTexture() {
const canvas = document.createElement( 'canvas' );
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(32, 32, 29, 0, 2 * Math.PI);
ctx.lineWidth = 5;
ctx.stroke();
ctx.fillStyle = "white";
ctx.fill();
return canvas;
}