追è¨
両æã®ã³ã³ããã¼ã©ã¼ã«å¯¾å¿ãã¾ããï¼
ãã¨ã®çºç«¯
examplesãè¦ãªããWebVRã³ã³ãã³ããã¤ãã£ã¦ããã®ã§ãããã³ã³ããã¼ã©ã¼ã®ãããªã¬ã¼ããã°ãªãããã使ããµã³ãã«ã¯ããã©ããã¹ãã£ãã¯ãããAããBããXããYããã¿ã³ã®åå¾ã®æ¹æ³ãããããªãã£ãã®ã§ããã¡ããã¡ã調ã¹ãçµæãã¾ã¨ãã¾ããã
çµè«
controllerModelFactory.createControllerModel
ãå®è¡ããéã«è¿ã£ã¦ããã¢ãã«ã«motionControllerããã£ã¤ãã¦ããã®ã§ã
controllerModel.motionController.data
ã«ã¢ã¯ã»ã¹ããã°ããããªã¬ã¼ããã°ãªããããã¹ãã£ãã¯ããAããBããXããYãã«ã¢ã¯ã»ã¹ã§ããã
ãã ããæ£ããåå¾æ¹æ³ãã¯è¬ã
DEMO
ãENTER VRããæ¼ä¸ããã¨ãç®ã®åã«ã³ã³ããã¼ã©ã¼ï¼1ã¤ã®ã¿ï¼ã®ç¶æ ã表示ããã¾ãã
ãã¿ã³ãæ¼ãã¨ç®ã®åã®æ å ±ãæ´æ°ããã¾ãã
controllerModel.motionController.dataãèç¼ã§ç¢ºèªããã¨ããã
[ { "id": "xr-standard-trigger", "state": "default", // default | touched | pressed "button": 0 // 0 ~ 1 }, { "id": "xr-standard-squeeze", "state": "default", // default | touched | pressed "button": 0 // 0 ~ 1 }, { "id": "xr-standard-thumbstick", "state": "default", // default | touched | pressed "button": 0, // 0 | 1 "xAxis": 0, // -1 ~ 1 "yAxis": 0 // -1 ~ 1 }, { "id": "a-button", // å·¦ã®ã¨ã㯠x-button "state": "default", // default | touched | pressed "button": 0 // 0 | 1 }, { "id": "b-button", // å·¦ã®ã¨ã㯠y-button "state": "default", // default | touched | pressed "button": 0 // 0 | 1 }, { "id": "thumbrest", // ? "state": "default", "button": 0 } ]
ã¨ããå¤ãå
¥ã£ã¦ã¾ããã
ã½ã¼ã¹ã³ã¼ã
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>webvr controller</title> <style> #VRButton { background: #000 !important; // è¦ã«ããã®ã§é»ã«ãã } </style> </head> <body> <script type="module"> import * as THREE from './build/three.module.js'; import { VRButton } from './jsm/webxr/VRButton.js'; import { XRControllerModelFactory } from './jsm/webxr/XRControllerModelFactory.js'; const canvas = document.createElement('canvas'); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setClearColor(0xf8f8f8, 1); renderer.vr.enabled = true; const controllerModelFactory = new XRControllerModelFactory(); const controllerGrip = renderer.xr.getControllerGrip(0); const controllerModel = controllerModelFactory.createControllerModel(controllerGrip); controllerGrip.add(controllerModel); // å ¬å¼ã«æä¾ããã¦ããããªã¬ã¼ãã°ãªããã®ã³ã¼ã«ããã¯ã¤ãã³ã controllerGrip.addEventListener('select', (evt) => console.log(evt)); controllerGrip.addEventListener('selectstart', (evt) => console.log(evt)); controllerGrip.addEventListener('selectend', (evt) => console.log(evt)); controllerGrip.addEventListener('squeeze', (evt) => console.log(evt)); controllerGrip.addEventListener('squeezestart', (evt) => console.log(evt)); controllerGrip.addEventListener('squeezeend', (evt) => console.log(evt)); controllerGrip.addEventListener('end', (evt) => console.log(evt)); scene.add(controllerGrip); renderer.setAnimationLoop(() => { renderer.render(scene, camera); if (debug && controllerModel.motionController) { // controllerModel.motionController.dataããgamepadã®ç¶æ ãåå¾ debug.log(JSON.stringify(controllerModel.motionController.data, null, ' ')); } }); // VR空éã«ãã°ã表示ããä»çµã¿ const debug = (() => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const fontSize = 24; const padding = 12; canvas.width = 600; canvas.height = 1200; const geometry = new THREE.PlaneGeometry(canvas.width / 100, canvas.height / 100); const texture = new THREE.CanvasTexture(canvas); const material = new THREE.MeshBasicMaterial({ map: texture }); const mesh = new THREE.Mesh(geometry, material); mesh.log = log; function log(text) { ctx.fillStyle = '#282828'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#f8f8f8'; ctx.font = `${fontSize}px sans-serif`; text.split('\n').map((text, i) => { ctx.fillText(text, padding, padding + fontSize * (i + 1)); }) material.map.needsUpdate = true; } return mesh; })(); debug.position.set(0, 0, -12); scene.add(debug); document.body.appendChild(VRButton.createButton(renderer)); </script> </body> </html>
çµç·¯
ã¾ãã¯ãドキュメントã«ãã³ããç¡ãããæ¢ãã¦ã¿ããã§ãããããã¨ã¿ãæãè¦ã¤ããããçµå±ソースコードãèªã¿ã¾ããã
ã¾ããgetControllerGripã®å¦çã調ã¹ãã¨ãWebXRControllerã®ã¤ã³ã¹ã¿ã³ã¹ãè¿ãã¦ãããã¨ããããã¾ãã
this.getControllerGrip = function ( index ) { let controller = controllers[ index ]; if ( controller === undefined ) { controller = new WebXRController(); // webXRControllerãè¿ãã¦ãã controllers[ index ] = controller; } return controller.getGripSpace(); };
ã¤ãã«ãWebXRControllerã®updateãè¦ã¦ã¿ãã¨ãå¼æ°ã§inputSourceãåãåã£ã¦ãããã¨ããããã¾ãã
update( inputSource, frame, referenceSpace ) {
ãã®inputSourceã®ä¸èº«ãconsole.logã§ç¢ºèªããã¨ããã
XRInputSource Symbol(@@webxr-polyfill/XRInputSource): {impl: GamepadXRInputSource, gripSpace: XRSpace, targetRaySpace: XRSpace} gamepad: (...) gripSpace: (...) handedness: (...) profiles: (...) targetRayMode: (...) targetRaySpace: (...)
ã¨ãgamepadã¨ãããªãã¸ã§ã¯ããå ¥ã£ã¦ããããªãã¸ã§ã¯ããããããªã¬ã¼ããã°ãªããããã¹ãã£ãã¯ããAããBããXããYãã®æ å ±ãèªã¿åããã®ã§ãå½åã¯ããã«dispatchã追å ãã¦åå¾ãããã¨èãã¾ããããThree.jsã®ã½ã¼ã¹ãç·¨éããã®ã¯å«ãªã®ã§è«¦ãã¾ããã
ã§ãxrInputSourceãåå¾ããæ¹æ³ãæ¢ãã¦ã¿ãã¨ããã
class MotionController { /** * @param {Object} xrInputSource - The XRInputSource to build the MotionController around * @param {Object} profile - The best matched profile description for the supplied xrInputSource * @param {Object} assetUrl */ constructor(xrInputSource, profile, assetUrl) {
MotionControllerã¨ããã¯ã©ã¹ãçºè¦ã
MotionControllerã®ã¤ã³ã¹ã¿ã³ã¹ãçæãã¦ããã¨ãããæ¢ãã¨ã
class XRControllerModelFactory { constructor( gltfLoader = null ) { this.gltfLoader = gltfLoader; this.path = DEFAULT_PROFILES_PATH; this._assetCache = {}; // If a GLTFLoader wasn't supplied to the constructor create a new one. if ( ! this.gltfLoader ) { this.gltfLoader = new GLTFLoader(); } } createControllerModel( controller ) { const controllerModel = new XRControllerModel(); let scene = null; controller.addEventListener( 'connected', ( event ) => { const xrInputSource = event.data; if ( xrInputSource.targetRayMode !== 'tracked-pointer' || ! xrInputSource.gamepad ) return; fetchProfile( xrInputSource, this.path, DEFAULT_PROFILE ).then( ( { profile, assetPath } ) => { controllerModel.motionController = new MotionController( xrInputSource, profile, assetPath );
ã¨ãcontrollerModelã®motionControllerã«ä»£å ¥ããã¦ããã¨ãããçºè¦ã
ã¨ããæµãã§ãcontrollerModelFactory.createControllerModelã§è¿ã£ã¦ããã¢ãã«ã®motionControllerã«ã¢ã¯ã»ã¹ããã¨ããææ³ãåããã¨ã«ãªã£ã次第ã§ãã