├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── asset-bug-report.md │ ├── motioncontroller-library-bug-report.md │ ├── new-asset-request.md │ ├── registry-change-request.md │ └── viewer-bug-report.md └── workflows │ └── main.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── babel.config.js ├── jest.config.js ├── lerna.json ├── old.travis.yml ├── package-lock.json ├── package.json ├── packages ├── assets │ ├── LICENSE.md │ ├── README.md │ ├── gulpTasks │ │ ├── buildProfilesTask.js │ │ ├── copySchemasTask.js │ │ ├── copyToolsTask.js │ │ ├── taskPaths.js │ │ └── writeProfilesListTask.js │ ├── gulpfile.js │ ├── package.json │ ├── profiles │ │ ├── generic-button │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-fixed-hand │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-hand │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-touchpad │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-squeeze-thumbstick │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-squeeze-touchpad-thumbstick │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-squeeze-touchpad │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-squeeze │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-thumbstick │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-touchpad-thumbstick │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger-touchpad │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── generic-trigger │ │ │ ├── left.glb │ │ │ ├── none.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── google-daydream │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── hp-mixed-reality │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── htc-vive-cosmos │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── htc-vive-focus-3 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── htc-vive-focus-plus │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── htc-vive-focus │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── htc-vive │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── logitech-mx-ink │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── magicleap-one │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── magicleap-two │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── meta-quest-touch-plus │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── meta-quest-touch-pro │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── microsoft-mixed-reality │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── oculus-go │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── oculus-touch-v2 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── oculus-touch-v3 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── oculus-touch │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── pico-4 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── pico-g2 │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── pico-neo2 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── pico-neo3 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── samsung-gearvr │ │ │ ├── none.glb │ │ │ └── profile.json │ │ ├── samsung-odyssey │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── valve-index │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ ├── yvr-touch-v2 │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ │ └── yvr-touch │ │ │ ├── left.glb │ │ │ ├── profile.json │ │ │ └── right.glb │ ├── schemas │ │ ├── common.schema.json │ │ ├── layout.schema.json │ │ ├── profile.schema.json │ │ └── visualResponses.schema.json │ ├── src │ │ ├── buildAssetProfile.js │ │ └── expandRegistryProfile.js │ ├── stats │ │ └── index.html │ └── tutorial │ │ ├── README.md │ │ └── images │ │ ├── tutorial-1.png │ │ ├── tutorial-10.png │ │ ├── tutorial-11.png │ │ ├── tutorial-12.png │ │ ├── tutorial-13.png │ │ ├── tutorial-14.png │ │ ├── tutorial-15.png │ │ ├── tutorial-16.png │ │ ├── tutorial-17.png │ │ ├── tutorial-18.png │ │ ├── tutorial-19.png │ │ ├── tutorial-2.png │ │ ├── tutorial-20.png │ │ ├── tutorial-21.png │ │ ├── tutorial-22.png │ │ ├── tutorial-23.png │ │ ├── tutorial-24.png │ │ ├── tutorial-25.png │ │ ├── tutorial-26.png │ │ ├── tutorial-27.png │ │ ├── tutorial-3.png │ │ ├── tutorial-4.png │ │ ├── tutorial-5.png │ │ ├── tutorial-6.png │ │ ├── tutorial-7.png │ │ ├── tutorial-8.png │ │ └── tutorial-9.png ├── motion-controllers │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── __tests__ │ │ │ ├── component.test.js │ │ │ ├── motionController.test.js │ │ │ ├── profiles.test.js │ │ │ └── visualResponse.test.js │ │ ├── component.d.ts │ │ ├── components.js │ │ ├── constants.d.ts │ │ ├── constants.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── motionController.d.ts │ │ ├── motionController.js │ │ ├── profiles.d.ts │ │ ├── profiles.js │ │ ├── visualResponse.d.ts │ │ └── visualResponse.js │ └── tsconfig.json ├── registry │ ├── LICENSE.md │ ├── README.md │ ├── gulpTasks │ │ ├── copySchemasTask.js │ │ ├── copyToolsTask.js │ │ ├── taskPaths.js │ │ ├── validateProfilesTask.js │ │ └── writeProfilesListTask.js │ ├── gulpfile.js │ ├── package.json │ ├── profiles │ │ ├── generic │ │ │ ├── generic-button.json │ │ │ ├── generic-fixed-hand.json │ │ │ ├── generic-hand-select-grasp.json │ │ │ ├── generic-hand-select.json │ │ │ ├── generic-hand.json │ │ │ ├── generic-touchpad.json │ │ │ ├── generic-touchscreen.json │ │ │ ├── generic-trigger-squeeze-thumbstick.json │ │ │ ├── generic-trigger-squeeze-touchpad-thumbstick.json │ │ │ ├── generic-trigger-squeeze-touchpad.json │ │ │ ├── generic-trigger-squeeze.json │ │ │ ├── generic-trigger-thumbstick.json │ │ │ ├── generic-trigger-touchpad-thumbstick.json │ │ │ ├── generic-trigger-touchpad.json │ │ │ └── generic-trigger.json │ │ ├── google │ │ │ └── google-daydream.json │ │ ├── hp │ │ │ └── hp-mixed-reality.json │ │ ├── htc │ │ │ ├── htc-vive-cosmos.json │ │ │ ├── htc-vive-focus-3.json │ │ │ ├── htc-vive-focus-plus.json │ │ │ ├── htc-vive-focus.json │ │ │ └── htc-vive.json │ │ ├── logitech │ │ │ └── logitech-mx-ink.json │ │ ├── magicleap │ │ │ ├── magicleap-one.json │ │ │ └── magicleap-two.json │ │ ├── meta │ │ │ ├── meta-fixed-hand.json │ │ │ ├── meta-quest-touch-plus.json │ │ │ └── meta-quest-touch-pro.json │ │ ├── microsoft │ │ │ └── microsoft-mixed-reality.json │ │ ├── oculus │ │ │ ├── oculus-go.json │ │ │ ├── oculus-hand.json │ │ │ ├── oculus-touch-v2.json │ │ │ ├── oculus-touch-v3.json │ │ │ └── oculus-touch.json │ │ ├── pico │ │ │ ├── pico-4.json │ │ │ ├── pico-g2.json │ │ │ ├── pico-neo2.json │ │ │ └── pico-neo3.json │ │ ├── samsung │ │ │ ├── samsung-gearvr.json │ │ │ └── samsung-odyssey.json │ │ ├── valve │ │ │ └── valve-index.json │ │ └── yvr │ │ │ ├── yvr-touch-v2.json │ │ │ └── yvr-touch.json │ ├── schemas │ │ ├── common.schema.json │ │ ├── gamepad.schema.json │ │ ├── layout.schema.json │ │ └── profile.schema.json │ └── src │ │ └── validateRegistryProfile.js └── viewer │ ├── LICENSE.md │ ├── README.md │ ├── backgrounds │ ├── README.md │ ├── autumn_forest_01_2k.hdr │ ├── backgrounds.json │ ├── cave_wall_2k.hdr │ ├── fireplace_2k.hdr │ ├── georgentor_2k.hdr │ ├── snowy_park_01_2k.hdr │ └── studio_small_03_2k.hdr │ ├── package.json │ ├── rollup.config.js │ └── src │ ├── .eslintrc.json │ ├── assetError.js │ ├── backgroundSelector.js │ ├── controllerModel.js │ ├── index.html │ ├── localProfile.js │ ├── manualControls.js │ ├── mocks │ ├── mockGamepad.js │ └── mockXRInputSource.js │ ├── modelViewer.js │ ├── profileSelector.js │ └── profileViewer.css └── w3c.json /.eslintignore: -------------------------------------------------------------------------------- 1 | /**/dist/** 2 | node_modules -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "airbnb-base" 8 | ], 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | "linebreak-style": ["warn", "unix"], 19 | "comma-dangle": ["error", "never"], 20 | "import/no-extraneous-dependencies": ["error", {"packageDir": "./"}], 21 | "import/prefer-default-export": ["none"] 22 | }, 23 | "overrides": [ 24 | { 25 | "files": [ 26 | "**/__tests__/**" 27 | ], 28 | "env": { 29 | "jest": true 30 | }, 31 | "plugins": ["jest"], 32 | "globals": { 33 | "expect": true, 34 | "test": true, 35 | "describe": true, 36 | "beforeAll": true, 37 | "beforeEach": true, 38 | "TestHelpers": "readonly" 39 | }, 40 | "rules": { 41 | "jest/no-disabled-tests": "warn", 42 | "jest/no-focused-tests": "error", 43 | "jest/no-identical-title": "error", 44 | "jest/prefer-to-have-length": "warn", 45 | "jest/valid-expect": "error", 46 | "import/no-extraneous-dependencies": [ 47 | "error", 48 | { 49 | "packageDir": ["./packages/motion-controllers/", "./"] 50 | } 51 | ] 52 | } 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/asset-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Asset bug report 3 | about: Report an issue with a 3D asset 4 | title: '' 5 | labels: assets 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Profile id** 11 | The profile id impacted 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **3D engine (please complete the following information):** 30 | - Engine: [e.g. three.js, babylon.js] 31 | - Version [e.g. 22] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/motioncontroller-library-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: MotionController library bug report 3 | about: Report an issue with the motion-controller package 4 | title: '' 5 | labels: motion-controller 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Version** 27 | - motion-controllers version 28 | - assets version 29 | 30 | **Desktop (please complete the following information):** 31 | - OS: [e.g. iOS] 32 | - 3D engine: [e.g. three.js, babylon.js] 33 | - Browser [e.g. chrome, safari] 34 | - Version [e.g. 22] 35 | 36 | **Smartphone (please complete the following information):** 37 | - Device: [e.g. iPhone6] 38 | - OS: [e.g. iOS8.1] 39 | - Browser [e.g. stock browser, safari] 40 | - Version [e.g. 22] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-asset-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New asset request 3 | about: Request an additional 3D asset be added to the repository 4 | title: '' 5 | labels: assets 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Profile Id** 11 | The profile id for which the asset should be added 12 | 13 | **Do you have an existing MIT licensed 3D asset to submit** 14 | Yes/No 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/registry-change-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Registry change request 3 | about: Report registry error or request new hardware profile 4 | title: '' 5 | labels: registry 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Profile ID** 11 | The ID of the profile 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expect the profile to contain. 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/viewer-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Viewer bug report 3 | about: Report a bug with the viewer package 4 | title: '' 5 | labels: viewer 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Checkout dependencies and deploy 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [ main ] 8 | pull_request: 9 | branches: [ main ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - name: Checkout 🛎️ 25 | uses: actions/checkout@v4 26 | 27 | # Installs NPM dependencies 28 | - name: Install 🔧 29 | run: npm install 30 | 31 | # Run Tests 32 | - name: Test 🧪 33 | run: npm run test 34 | 35 | - name: Deploy 🚀 36 | uses: Cecilapp/GitHub-Pages-deploy@v3 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | email: tojiro@gmail.com 41 | build_dir: . 42 | jekyll: yes 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | node_modules 4 | npm-debug*.log* 5 | lerna*.log 6 | dist 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Unless otherwise specified in individual packages, all documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebXR Input Profiles 2 | 3 | [![Build Status](https://travis-ci.com/immersive-web/webxr-input-profiles.svg?branch=master)](https://travis-ci.org/immersive-web/webxr-input-profiles) 4 | 5 | ## Repository 6 | 7 | This repository contains information necessary for User Agents to have conformance in WebXR `XRInputSource` objects for all known hardware devices. It also contains assets and a helper library for developers to visualize motion controllers reported though `XRInputSource` objects. A preview page is also included to allow end-to-end validation of new hardware. The master branch of this preview page is hosted on Github here: 8 | 9 | **[Profile Validator and Viewer](https://immersive-web.github.io/webxr-input-profiles/packages/viewer/dist/index.html)** 10 | 11 | ## Packages 12 | * The [registry](./packages/registry/README.md) package contains JSON files which define the intrinsic values for each type of `XRInputSource` hardware to ensure User Agent conformity. 13 | * The [assets](./packages/assets/README.md) package contains 3D assets and JSON files to describe the relationship between those assets and the associated `XRInputSource` profiles defined in the [registry](./packages/assets/README.md). The build step of this package merges its content with the JSON files in the [registry](./packages/registry/README.md) package. 14 | * The [motion-controllers](./packages/motion-controllers/README.md) package contains a javascript library able to load the JSON descriptions published from the [assets](./packages/assets/README.md) package and create component-style representations of the `XRInputSource` data. Specifically, it maps the the [`Gamepad` data exposed by the `XRInputSource`](https://immersive-web.github.io/webxr-gamepads-module/). This library is 3D engine agnostic. 15 | * The [viewer](./packages/viewer/README.md) package contains a webpage that uses the [motion-controllers](./packages/motion-controllers/README.md) library to load and view the profiles and assets from the [assets](./packages/assets/README.md) package. 16 | 17 | ## Versioning 18 | Packages will be published to npm as changes occur, with version numbers formatted as `..` and updated according to the following guildlines: 19 | 20 | ### Major 21 | - Significant design changes 22 | 23 | ### Minor 24 | - Additional features added 25 | - Small breaking changes to schema 26 | - Breaking changes in source code or test code 27 | 28 | ### Patch 29 | - Adds new mapping and/or asset files 30 | - Fixes to existing mapping and/or asset files 31 | - Critical, non-breaking security fixes 32 | - Occasional non-breaking fixes to schema, source code, or test code 33 | 34 | Packages from this repo may update their `Minor` and `Patch` versions at a different cadence. Changes to `Major` versions are expected to be large enough that all packages will update in tandem. 35 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | 4 | const presets = [ 5 | [ 6 | '@babel/preset-env', 7 | { 8 | targets: { 9 | node: 'current' 10 | } 11 | } 12 | ] 13 | ]; 14 | 15 | const plugins = [ 16 | ['@babel/plugin-transform-spread', { loose: true }] 17 | ]; 18 | 19 | return { presets, plugins }; 20 | }; 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.jsx?$': 'babel-jest' 4 | }, 5 | testMatch: [ 6 | '/**/*.test.js' 7 | ] 8 | }; 9 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /old.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '12' 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | script: 11 | - npm run test 12 | 13 | deploy: 14 | provider: pages 15 | skip-cleanup: true 16 | github-token: $GH_TOKEN 17 | on: 18 | branch: main 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webxr-input-profiles", 3 | "version": "1.0.1", 4 | "description": "A tool for making W3C Gamepad objects easier to work with in XR on the web", 5 | "author": "Nell Waliczek ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/immersive-web/webxr-input-profiles.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/immersive-web/webxr-input-profiles/issues" 13 | }, 14 | "homepage": "https://immersive-web.github.io/webxr-input-profiles", 15 | "keywords": [ 16 | "WebXR", 17 | "WebVR", 18 | "ImmersiveWeb", 19 | "Immersive Web", 20 | "AR", 21 | "Virtual Reality", 22 | "Augmented Reality", 23 | "Mixed Reality", 24 | "VR", 25 | "XR", 26 | "AR/VR", 27 | "VR/AR", 28 | "Gamepad", 29 | "Gamepads", 30 | "Motion controllers", 31 | "MotionControllers" 32 | ], 33 | "scripts": { 34 | "clean": "npx lerna clean --yes && npx lerna run clean && npx lerna bootstrap --hoist", 35 | "lint": "eslint ./", 36 | "lintFix": "eslint ./ --fix", 37 | "incrementalBuild": "npx lerna run build", 38 | "build": "npx lerna bootstrap --hoist && npm run incrementalBuild", 39 | "cleanBuild": "npm run clean && npm run incrementalBuild", 40 | "testOnly": "jest", 41 | "watch": "npx lerna run watch --no-sort --stream", 42 | "dev": "http-server ./packages/viewer/dist -c-1 -p 8080", 43 | "prepublishOnly": "npm run clean && npm run lint && npm run build", 44 | "pretest": "npm run prepublishOnly", 45 | "test": "npx lerna run typecheck && npm run testOnly" 46 | }, 47 | "devDependencies": { 48 | "@babel/plugin-transform-spread": "^7.2.2", 49 | "@babel/preset-env": "^7.3.4", 50 | "@types/fetch-mock": "^7.3.2", 51 | "@types/jest": "^24.9.0", 52 | "ajv": "^6.10.0", 53 | "babel-jest": "^24.1.0", 54 | "eslint": "^5.16.0", 55 | "eslint-config-airbnb-base": "^13.1.0", 56 | "eslint-plugin-import": "^2.17.2", 57 | "eslint-plugin-jest": "^22.15.2", 58 | "fetch-mock": "^8.3.1", 59 | "fs-extra": "^8.1.0", 60 | "glob": "^7.1.4", 61 | "gulp": "^4.0.2", 62 | "gulp-jsoncombine": "^1.0.4", 63 | "http-server": "^0.12.1", 64 | "jest": "^24.1.0", 65 | "lerna": "^3.16.4", 66 | "node-fetch": "^2.6.0", 67 | "optional-require": "^1.0.0", 68 | "rollup": "^1.4.0", 69 | "rollup-plugin-copy-glob": "^0.3.1", 70 | "rollup-plugin-license": "^0.13.0", 71 | "three": "^0.127.0", 72 | "through2": "^3.0.1", 73 | "typescript": "^3.7.5" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/assets/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Amazon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice (including the next 13 | paragraph) shall be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 21 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/assets/gulpTasks/buildProfilesTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const path = require('path'); 3 | const fs = require('fs-extra'); 4 | const Ajv = require('ajv'); 5 | const through = require('through2'); 6 | 7 | const optionalRequire = require('optional-require')(require); 8 | 9 | const registryModulePath = optionalRequire.resolve('@webxr-input-profiles/registry', ''); 10 | 11 | const taskPaths = require('./taskPaths'); 12 | const expandRegistryProfile = require('../src/expandRegistryProfile'); 13 | const buildAssetProfile = require('../src/buildAssetProfile'); 14 | 15 | /** 16 | * Validate the profile against the schema 17 | * @param {object} profileJson - the profile to validate 18 | */ 19 | function buildValidator() { 20 | const ajv = new Ajv(); 21 | const schemasPath = path.join(taskPaths.toolsDest, taskPaths.schemasCombinedFilename); 22 | const schemas = fs.readJsonSync(schemasPath); 23 | schemas.dependencies.forEach((schema) => { 24 | ajv.addSchema(schema); 25 | }); 26 | return ajv.compile(schemas.mainSchema); 27 | } 28 | 29 | function validateAssetInfo(schemaValidator) { 30 | return through.obj((vinylFile, encoding, callback) => { 31 | const assetInfo = JSON.parse(vinylFile.contents.toString()); 32 | 33 | // Validate the supplied profile 34 | if (!schemaValidator(assetInfo)) { 35 | const errors = JSON.stringify(schemaValidator.errors, null, 2); 36 | throw new Error(`${assetInfo.id} Failed to validate schema with errors: ${errors}`); 37 | } 38 | 39 | callback(null, vinylFile); 40 | }); 41 | } 42 | 43 | function buildProfile() { 44 | return through.obj((vinylFile, encoding, callback) => { 45 | const profileId = path.basename(vinylFile.dirname); 46 | const vendorId = profileId.split('-', 1)[0]; 47 | 48 | // Get the matching file from the registry 49 | const registryFolder = path.join(path.dirname(registryModulePath), 'profiles'); 50 | const registryJson = fs.readJsonSync(path.join(registryFolder, vendorId, `${profileId}.json`)); 51 | const expandedRegistryProfile = expandRegistryProfile(registryJson); 52 | 53 | const assetInfo = JSON.parse(vinylFile.contents.toString()); 54 | const assetProfile = buildAssetProfile(assetInfo, expandedRegistryProfile); 55 | const outputFile = vinylFile.clone(); 56 | outputFile.contents = Buffer.from(JSON.stringify(assetProfile, null, 2)); 57 | 58 | callback(null, outputFile); 59 | }); 60 | } 61 | 62 | function buildProfilesTask() { 63 | const schemaValidator = buildValidator(); 64 | 65 | return gulp.src(taskPaths.profilesGlob) 66 | .pipe(validateAssetInfo(schemaValidator)) 67 | .pipe(buildProfile()) 68 | .pipe(gulp.dest(taskPaths.profilesDest)); 69 | } 70 | 71 | module.exports = buildProfilesTask; 72 | -------------------------------------------------------------------------------- /packages/assets/gulpTasks/copySchemasTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const path = require('path'); 3 | const jsonCombine = require('gulp-jsoncombine'); 4 | 5 | const taskPaths = require('./taskPaths'); 6 | 7 | const mainSchemaFilename = 'profile.schema.json'; 8 | 9 | function combineSchemas(input) { 10 | const mainSchemaKey = path.basename(mainSchemaFilename, '.json'); 11 | const mainSchema = input[mainSchemaKey]; 12 | const dependencies = []; 13 | Object.keys(input).forEach((key) => { 14 | if (key !== mainSchemaKey) { 15 | dependencies.push(input[key]); 16 | } 17 | }); 18 | 19 | const schemas = { 20 | mainSchema, 21 | dependencies 22 | }; 23 | return Buffer.from(JSON.stringify(schemas, null, 2)); 24 | } 25 | 26 | function copySchemas() { 27 | return gulp.src(taskPaths.schemasGlob) 28 | .pipe(gulp.dest(taskPaths.schemasDest)) 29 | .pipe(jsonCombine(taskPaths.schemasCombinedFilename, combineSchemas)) 30 | .pipe(gulp.dest(taskPaths.toolsDest)); 31 | } 32 | 33 | module.exports = copySchemas; 34 | -------------------------------------------------------------------------------- /packages/assets/gulpTasks/copyToolsTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const through = require('through2'); 3 | const path = require('path'); 4 | 5 | const taskPaths = require('./taskPaths'); 6 | 7 | function convertMergeScript() { 8 | return through.obj((vinylFile, encoding, callback) => { 9 | const newFile = vinylFile.clone(); 10 | if (path.extname(vinylFile.relative) === '.js') { 11 | let fileContents = vinylFile.contents.toString(); 12 | fileContents = fileContents.replace('module.exports =', 'export default'); 13 | newFile.contents = Buffer.from(fileContents); 14 | } 15 | callback(null, newFile); 16 | }); 17 | } 18 | 19 | function copyProfilesTools() { 20 | return gulp.src(taskPaths.toolsGlob) 21 | .pipe(convertMergeScript()) 22 | .pipe(gulp.dest(taskPaths.toolsDest)); 23 | } 24 | 25 | module.exports = copyProfilesTools; 26 | -------------------------------------------------------------------------------- /packages/assets/gulpTasks/taskPaths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const taskPaths = {}; 4 | 5 | taskPaths.dest = path.join(__dirname, '../dist'); 6 | 7 | taskPaths.profilesSrc = path.join(__dirname, '../profiles'); 8 | taskPaths.profilesGlob = path.join(taskPaths.profilesSrc, '**/profile.json'); 9 | taskPaths.profilesDest = path.join(taskPaths.dest, 'profiles'); 10 | 11 | taskPaths.assetsGlob = ['profiles/**', `!${taskPaths.profilesGlob}`]; 12 | taskPaths.assetsDest = taskPaths.profilesDest; 13 | 14 | taskPaths.schemasSrc = path.join(__dirname, '../schemas'); 15 | taskPaths.schemasGlob = path.join(taskPaths.schemasSrc, '**'); 16 | taskPaths.schemasDest = path.join(taskPaths.dest, 'schemas'); 17 | taskPaths.schemasCombinedFilename = 'assetSchemas.json'; 18 | 19 | taskPaths.toolsSrc = path.join(__dirname, '../src'); 20 | taskPaths.toolsGlob = path.join(taskPaths.toolsSrc, '*.js'); 21 | taskPaths.toolsDest = path.join(taskPaths.dest, 'profilesTools'); 22 | 23 | taskPaths.watchGlobs = [taskPaths.profilesGlob, taskPaths.schemasGlob]; 24 | 25 | module.exports = taskPaths; 26 | -------------------------------------------------------------------------------- /packages/assets/gulpTasks/writeProfilesListTask.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const glob = require('glob'); 4 | 5 | const optionalRequire = require('optional-require')(require); 6 | 7 | const taskPaths = require('./taskPaths'); 8 | 9 | const registryModulePath = optionalRequire.resolve('@webxr-input-profiles/registry', ''); 10 | const profilesListDest = path.join(taskPaths.profilesDest, 'profilesList.json'); 11 | 12 | function generate() { 13 | return new Promise((resolve, reject) => { 14 | const profilesList = {}; 15 | glob(taskPaths.profilesGlob, null, (error, files) => { 16 | if (error) { 17 | reject(error); 18 | } else { 19 | files.forEach((file) => { 20 | const profileId = path.basename(path.dirname(file)); 21 | const relativePath = file.substr((taskPaths.profilesSrc.length) + 1); 22 | profilesList[profileId] = { path: relativePath }; 23 | 24 | // If there are any deprecated profile ids listed in the registry file, 25 | // list them as pointing to the same path as the standard profile id. 26 | const vendorId = profileId.split('-', 1)[0]; 27 | const registryFolder = path.join(path.dirname(registryModulePath), 'profiles'); 28 | const registryJson = fs.readJsonSync(path.join(registryFolder, vendorId, `${profileId}.json`)); 29 | if (registryJson.deprecatedProfileIds) { 30 | registryJson.deprecatedProfileIds.forEach((deprecatedId) => { 31 | profilesList[deprecatedId] = { 32 | path: relativePath, 33 | deprecated: true 34 | }; 35 | }); 36 | } 37 | }); 38 | 39 | resolve(profilesList); 40 | } 41 | }); 42 | }); 43 | } 44 | 45 | function writeProfilesList() { 46 | return generate().then((profilesList) => { 47 | fs.outputJson(profilesListDest, profilesList, { spaces: 2 }); 48 | }); 49 | } 50 | 51 | module.exports = writeProfilesList; 52 | -------------------------------------------------------------------------------- /packages/assets/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const fs = require('fs-extra'); 3 | 4 | const taskPaths = require('./gulpTasks/taskPaths'); 5 | const buildProfiles = require('./gulpTasks/buildProfilesTask'); 6 | const copySchemas = require('./gulpTasks/copySchemasTask'); 7 | const copyTools = require('./gulpTasks/copyToolsTask'); 8 | const writeProfilesList = require('./gulpTasks/writeProfilesListTask'); 9 | 10 | function clean() { 11 | return fs.remove(taskPaths.dest); 12 | } 13 | 14 | function copyAssets() { 15 | return gulp.src(taskPaths.assetsGlob).pipe(gulp.dest(taskPaths.assetsDest)); 16 | } 17 | 18 | const build = gulp.series( 19 | copySchemas, 20 | buildProfiles, 21 | gulp.parallel(copyAssets, copyTools, writeProfilesList) 22 | ); 23 | 24 | const cleanBuild = gulp.series(clean, build); 25 | 26 | function watchBuild() { 27 | return gulp.watch(taskPaths.watchGlobs, { ignoreInitial: false }, cleanBuild); 28 | } 29 | 30 | exports.clean = clean; 31 | exports.build = build; 32 | exports.cleanBuild = cleanBuild; 33 | exports.watch = watchBuild; 34 | 35 | exports.default = cleanBuild; 36 | -------------------------------------------------------------------------------- /packages/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webxr-input-profiles/assets", 3 | "version": "1.0.17", 4 | "description": "assets", 5 | "main": "dist/profilesList.json", 6 | "files": [ 7 | "package.json", 8 | "LICENSE", 9 | "README.md", 10 | "dist/profiles/", 11 | "schemas/", 12 | "src/**" 13 | ], 14 | "scripts": { 15 | "clean": "gulp clean", 16 | "build": "gulp", 17 | "cleanBuild": "npm run clean && npm run build", 18 | "watch": "gulp watch", 19 | "test": "echo \"Run tests from root\" && exit 1" 20 | }, 21 | "devDependencies": { 22 | "@webxr-input-profiles/registry": "^1.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/assets/profiles/generic-button/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-button/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-button/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-button/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-button/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-button", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-button/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-button/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-fixed-hand/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-fixed-hand/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-fixed-hand/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-fixed-hand", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/generic-fixed-hand/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-fixed-hand/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-hand/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-hand/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-hand/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-hand", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/generic-hand/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-hand/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-touchpad/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-touchpad/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-touchpad/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-touchpad/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-touchpad/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-touchpad", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-touchpad/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-touchpad/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-thumbstick/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-thumbstick/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-thumbstick/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-thumbstick/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-thumbstick/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-squeeze-thumbstick", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-thumbstick/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-thumbstick/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-squeeze-touchpad-thumbstick", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad-thumbstick/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-squeeze-touchpad", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze-touchpad/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze-touchpad/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-squeeze", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-squeeze/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-squeeze/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-thumbstick/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-thumbstick/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-thumbstick/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-thumbstick/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-thumbstick/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-thumbstick", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-thumbstick/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-thumbstick/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad-thumbstick/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad-thumbstick/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad-thumbstick/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad-thumbstick/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad-thumbstick/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-touchpad-thumbstick", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad-thumbstick/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad-thumbstick/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-touchpad", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger-touchpad/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger-touchpad/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/generic-trigger/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/generic-trigger/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/google-daydream/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/google-daydream/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/google-daydream/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "google-daydream", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "google_daydream_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/hp-mixed-reality/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/hp-mixed-reality/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/hp-mixed-reality/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "hp-mixed-reality", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/hp-mixed-reality/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/hp-mixed-reality/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-cosmos/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-cosmos/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-cosmos/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "htc-vive-cosmos", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-cosmos/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-cosmos/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus-3/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-focus-3/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus-3/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "htc-vive-focus-3", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus-3/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-focus-3/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus-plus/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-focus-plus/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus-plus/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "htc-vive-focus-plus", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "htc_vive_focus_plus_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive-focus/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive-focus/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "htc-vive-focus", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "htc_vive_focus_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/htc-vive/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/htc-vive/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "htc-vive", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "htc_vive_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/logitech-mx-ink/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/logitech-mx-ink/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/logitech-mx-ink/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "logitech-mx-ink", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "logitech_mx_ink_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/magicleap-one/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/magicleap-one/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/magicleap-one/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "magicleap-one", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "magicleap_one_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/assets/profiles/magicleap-two/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/magicleap-two/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/magicleap-two/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "magicleap-two", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "magicleap_two_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-plus/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/meta-quest-touch-plus/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-plus/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "meta-quest-touch-plus", 3 | "overrides": {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-plus/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/meta-quest-touch-plus/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-pro/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/meta-quest-touch-pro/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-pro/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "meta-quest-touch-pro", 3 | "overrides": {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/meta-quest-touch-pro/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/meta-quest-touch-pro/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/microsoft-mixed-reality/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/microsoft-mixed-reality/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/microsoft-mixed-reality/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "microsoft-mixed-reality", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/microsoft-mixed-reality/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/microsoft-mixed-reality/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-go/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-go/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-go/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "oculus-go", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "oculus_go_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v2/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch-v2/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v2/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "oculus-touch-v2", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v2/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch-v2/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v3/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch-v3/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v3/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "oculus-touch-v3", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch-v3/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch-v3/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "oculus-touch", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/oculus-touch/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/oculus-touch/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-4/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-4/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-4/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "pico-4", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/pico-4/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-4/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-g2/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-g2/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-g2/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "pico-g2", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "pico_g2_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo2/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-neo2/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo2/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "pico-neo2", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo2/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-neo2/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo3/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-neo3/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo3/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "pico-neo3", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/pico-neo3/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/pico-neo3/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/samsung-gearvr/none.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/samsung-gearvr/none.glb -------------------------------------------------------------------------------- /packages/assets/profiles/samsung-gearvr/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "samsung-gearvr", 3 | "overrides" : { 4 | "left-right": { 5 | "rootNodeName": "samsung_gearvr_none", 6 | "assetPath": "none.glb" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /packages/assets/profiles/samsung-odyssey/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/samsung-odyssey/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/samsung-odyssey/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "samsung-odyssey", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/samsung-odyssey/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/samsung-odyssey/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/valve-index/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/valve-index/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/valve-index/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "valve-index", 3 | "overrides" : {} 4 | } -------------------------------------------------------------------------------- /packages/assets/profiles/valve-index/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/valve-index/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch-v2/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/yvr-touch-v2/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch-v2/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "yvr-touch-v2", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch-v2/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/yvr-touch-v2/right.glb -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch/left.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/yvr-touch/left.glb -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "yvr-touch", 3 | "overrides" : {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/assets/profiles/yvr-touch/right.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/profiles/yvr-touch/right.glb -------------------------------------------------------------------------------- /packages/assets/schemas/common.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/assets/0.1.0/common.schema.json", 4 | "definitions": { 5 | "profileId": { 6 | "description": "A properly formatted string uniquely identifying a profile", 7 | "type": "string", 8 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)+$" 9 | }, 10 | "componentId": { 11 | "description": "A well-formatted component id", 12 | "type": "string", 13 | "pattern": "^[a-z0-9]+(_[a-z0-9]+)*$" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /packages/assets/schemas/layout.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/assets/0.1.0/layout.schema.json", 4 | "type": "object", 5 | "additionalProperties": false, 6 | "minProperties": 1, 7 | "properties": { 8 | "rootNodeName": { 9 | "type": "string", 10 | "pattern": "^[a-z0-9]+(_[a-z0-9]+)*$" 11 | }, 12 | "assetPath": { 13 | "type": "string", 14 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)*\\.glb$" 15 | }, 16 | "components": { 17 | "description": "The collection of components in the layout", 18 | "type": "object", 19 | "additionalProperties": false, 20 | "minProperties": 1, 21 | "patternProperties": { 22 | "^[a-z0-9]+(-[a-z0-9]+)*$": { 23 | "type": "object", 24 | "description": "Component description", 25 | "additionalProperties": false, 26 | "minProperties": 1, 27 | "properties": { 28 | "rootNodeName": { 29 | "type": "string", 30 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" 31 | }, 32 | "touchPointNodeName": { 33 | "type": "string", 34 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" 35 | }, 36 | "visualResponses": { "$ref": "visualResponses.schema.json"} 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /packages/assets/schemas/profile.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/assets/0.1.0/profile.schema.json", 4 | "type": "object", 5 | "description": "The root object for a webxr input profile asset description", 6 | "additionalProperties": false, 7 | "required": [ "profileId", "overrides" ], 8 | "properties": { 9 | "profileId" : { "$ref": "common.schema.json#/definitions/profileId" }, 10 | "overrides" : { 11 | "type": "object", 12 | "additionalProperties": false, 13 | "properties": { 14 | "none": { "$ref": "layout.schema.json" }, 15 | "left": { "$ref": "layout.schema.json" }, 16 | "right": { "$ref": "layout.schema.json" }, 17 | "left-right": { "$ref": "layout.schema.json" }, 18 | "left-right-none": { "$ref": "layout.schema.json" } 19 | }, 20 | "dependencies": { 21 | "left-right-none": { "not": { "required": ["none", "left", "right", "left-right"] } }, 22 | "left-right": { "not": { "required": ["left", "right", "left-right-none"] } }, 23 | "left": { "not": { "required": ["left-right", "left-right-none"] } }, 24 | "right": { "not": { "required": ["left-right", "left-right-none"] } }, 25 | "none": { "not": { "required": ["left-right-none"] } } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /packages/assets/schemas/visualResponses.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/assets/0.1.0/visualResponses.schema.json", 4 | "type": "object", 5 | "description": "The visual responses", 6 | "additionalProperties": false, 7 | "minProperties": 1, 8 | "patternProperties": { 9 | "^[a-z0-9]+(_[a-z0-9]+)*$": { 10 | "oneOf": [ 11 | { 12 | "type": "null" 13 | }, 14 | { 15 | "type": "object", 16 | "required": ["componentProperty", "states", "valueNodeProperty"], 17 | "additionalProperties": false, 18 | "properties": { 19 | "componentProperty": { 20 | "description": "The property name driving this visualization", 21 | "type": "string", 22 | "enum": ["x-axis", "y-axis", "button", "state"] 23 | }, 24 | "states": { 25 | "description": "The states the visualization applies to", 26 | "type": "array", 27 | "minItems": 1, 28 | "items": { 29 | "type": "string", 30 | "enum": ["default", "touched", "pressed"] 31 | } 32 | }, 33 | "valueNodeProperty": { 34 | "type": "string", 35 | "enum": ["transform", "visibility"] 36 | } 37 | }, 38 | "if": { 39 | "properties": { 40 | "componentProperty": { "enum":["state"] } 41 | } 42 | }, 43 | "then": { 44 | "properties": { 45 | "property": { 46 | "type": "string", 47 | "enum": ["transform", "visibility"] 48 | } 49 | } 50 | }, 51 | "else": { 52 | "properties": { 53 | "property": { 54 | "type": "string", 55 | "enum": ["transform"] 56 | } 57 | } 58 | } 59 | } 60 | ] 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /packages/assets/src/expandRegistryProfile.js: -------------------------------------------------------------------------------- 1 | function expandRegistryProfile(registryProfile) { 2 | const expandedProfile = { 3 | profileId: registryProfile.profileId, 4 | fallbackProfileIds: registryProfile.fallbackProfileIds, 5 | layouts: {} 6 | }; 7 | 8 | Object.keys(registryProfile.layouts).forEach((layoutId) => { 9 | // Split the layoutId string so a layout exists for each handedness separately 10 | (layoutId.split('-')).forEach((handedness) => { 11 | const layoutInfo = registryProfile.layouts[layoutId]; 12 | const layout = { 13 | selectComponentId: layoutInfo.selectComponentId, 14 | components: {} 15 | }; 16 | 17 | if (layoutInfo.gamepad) { 18 | layout.gamepadMapping = layoutInfo.gamepad.mapping; 19 | } 20 | 21 | Object.keys(layoutInfo.components).forEach((componentId) => { 22 | const component = { 23 | type: layoutInfo.components[componentId].type 24 | }; 25 | 26 | if (layoutInfo.gamepad) { 27 | component.gamepadIndices = {}; 28 | 29 | // Add button index to component 30 | const buttonIndex = layoutInfo.gamepad.buttons.indexOf(componentId); 31 | if (buttonIndex !== -1) { 32 | component.gamepadIndices.button = buttonIndex; 33 | } 34 | 35 | // Add axes indices to component 36 | layoutInfo.gamepad.axes.forEach((axisInfo, index) => { 37 | if (axisInfo !== null && axisInfo.componentId === componentId) { 38 | const axisIndexName = { 39 | 'x-axis': 'xAxis', 40 | 'y-axis': 'yAxis', 41 | 'z-axis': 'zAxis' 42 | }[axisInfo.axis]; 43 | 44 | component.gamepadIndices[axisIndexName] = index; 45 | } 46 | }); 47 | 48 | if (Object.keys(component.gamepadIndices).length > 0) { 49 | layout.components[componentId] = component; 50 | } 51 | } 52 | }); 53 | 54 | expandedProfile.layouts[handedness] = layout; 55 | }); 56 | }); 57 | 58 | return expandedProfile; 59 | } 60 | 61 | module.exports = expandRegistryProfile; 62 | -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-1.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-10.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-11.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-12.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-13.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-14.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-15.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-16.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-17.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-18.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-19.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-2.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-20.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-21.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-22.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-23.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-24.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-25.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-26.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-27.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-3.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-4.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-5.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-6.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-7.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-8.png -------------------------------------------------------------------------------- /packages/assets/tutorial/images/tutorial-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/assets/tutorial/images/tutorial-9.png -------------------------------------------------------------------------------- /packages/motion-controllers/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Amazon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice (including the next 13 | paragraph) shall be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 21 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/motion-controllers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webxr-input-profiles/motion-controllers", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "main": "./dist/motion-controllers.cjs", 7 | "module": "./dist/motion-controllers.js", 8 | "types": "./src/index.d.ts", 9 | "exports": { 10 | "types": "./src/index.d.ts", 11 | "require": "./dist/motion-controllers.cjs", 12 | "import": "./dist/motion-controllers.js" 13 | }, 14 | "files": [ 15 | "package.json", 16 | "LICENSE", 17 | "README.md", 18 | "dist/**", 19 | "src/**" 20 | ], 21 | "scripts": { 22 | "clean": "node -e \"try { require('fs').rmdirSync('./dist', { recursive: true }); } catch {}\"", 23 | "build": "rollup -c", 24 | "cleanBuild": "npm run clean && npm run build", 25 | "watch": "rollup -c -w -m inline", 26 | "test": "echo \"Run tests from root\" && exit 1", 27 | "typecheck": "tsc -p tsconfig.json" 28 | }, 29 | "devDependencies": { 30 | "@webxr-input-profiles/assets": "^1.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/motion-controllers/rollup.config.js: -------------------------------------------------------------------------------- 1 | const license = require('rollup-plugin-license'); 2 | 3 | const DIST_FOLDER = 'dist'; 4 | 5 | export default [ 6 | { 7 | plugins: [ 8 | license({ 9 | banner: '<%= pkg.name %> <%= pkg.version %> https://github.com/immersive-web/webxr-input-profiles' 10 | }) 11 | ], 12 | input: ['src/index.js'], 13 | output: [ 14 | { 15 | format: 'es', 16 | file: `${DIST_FOLDER}/motion-controllers.js` 17 | }, 18 | { 19 | format: 'cjs', 20 | file: `${DIST_FOLDER}/motion-controllers.cjs` 21 | } 22 | ] 23 | } 24 | ]; 25 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/__tests__/component.test.js: -------------------------------------------------------------------------------- 1 | import { Constants } from '../constants'; 2 | import { Component } from '../components'; 3 | 4 | const buttonComponent = { 5 | id: 'buttonComponent', 6 | description: { 7 | visualResponses: {}, 8 | gamepadIndices: { 9 | [Constants.ComponentProperty.BUTTON]: 0 10 | } 11 | } 12 | }; 13 | 14 | const axesComponent = { 15 | id: 'axesComponent', 16 | description: { 17 | visualResponses: {}, 18 | gamepadIndices: { 19 | [Constants.ComponentProperty.X_AXIS]: 0, 20 | [Constants.ComponentProperty.Y_AXIS]: 0 21 | } 22 | } 23 | }; 24 | 25 | const axesButtonComponent = { 26 | id: 'axesButtonComponent', 27 | description: { 28 | visualResponses: {}, 29 | gamepadIndices: { 30 | [Constants.ComponentProperty.BUTTON]: 0, 31 | [Constants.ComponentProperty.X_AXIS]: 0, 32 | [Constants.ComponentProperty.Y_AXIS]: 0 33 | } 34 | } 35 | }; 36 | 37 | const oneAxisComponent = { 38 | id: 'oneAxisComponent', 39 | description: { 40 | visualResponses: {}, 41 | gamepadIndices: { 42 | [Constants.ComponentProperty.Y_AXIS]: 0 43 | } 44 | } 45 | }; 46 | 47 | describe('Construction', () => { 48 | test('Button created', () => { 49 | const { id, description } = buttonComponent; 50 | const component = new Component(id, description); 51 | expect(component).toBeDefined(); 52 | expect(component.values.button).toEqual(0); 53 | expect(component.values.xAxis).toBeUndefined(); 54 | expect(component.values.yAxis).toBeUndefined(); 55 | expect(component.values.state).toEqual(Constants.ComponentState.DEFAULT); 56 | }); 57 | 58 | test('Thumbstick/touchpad created with no button', () => { 59 | const { id, description } = axesComponent; 60 | const component = new Component(id, description); 61 | expect(component).toBeDefined(); 62 | expect(component.values.button).toBeUndefined(); 63 | expect(component.values.xAxis).toEqual(0); 64 | expect(component.values.yAxis).toEqual(0); 65 | expect(component.values.state).toEqual(Constants.ComponentState.DEFAULT); 66 | }); 67 | 68 | test('Thumbstick/touchpad created with button', () => { 69 | const { id, description } = axesButtonComponent; 70 | const component = new Component(id, description); 71 | expect(component).toBeDefined(); 72 | expect(component.values.button).toEqual(0); 73 | expect(component.values.xAxis).toEqual(0); 74 | expect(component.values.yAxis).toEqual(0); 75 | expect(component.values.state).toEqual(Constants.ComponentState.DEFAULT); 76 | }); 77 | 78 | test('Thumbstick/touchpad created with only one axis', () => { 79 | const { id, description } = oneAxisComponent; 80 | const component = new Component(id, description); 81 | expect(component).toBeDefined(); 82 | expect(component.values.button).toBeUndefined(); 83 | expect(component.values.xAxis).toBeUndefined(); 84 | expect(component.values.yAxis).toEqual(0); 85 | expect(component.values.state).toEqual(Constants.ComponentState.DEFAULT); 86 | }); 87 | 88 | test('No id', () => { 89 | const componentDescription = {}; 90 | expect(() => { 91 | // eslint-disable-next-line no-unused-vars 92 | const component = new Component(null, componentDescription); 93 | }).toThrow(); 94 | 95 | expect(() => { 96 | // eslint-disable-next-line no-unused-vars 97 | const component = new Component(undefined, componentDescription); 98 | }).toThrow(); 99 | }); 100 | 101 | test('No componentDescription', () => { 102 | const componentId = 'test-component-id'; 103 | 104 | expect(() => { 105 | // eslint-disable-next-line no-unused-vars 106 | const component = new Component(componentId, null); 107 | }).toThrow(); 108 | 109 | expect(() => { 110 | // eslint-disable-next-line no-unused-vars 111 | const component = new Component(componentId, undefined); 112 | }).toThrow(); 113 | }); 114 | 115 | test('Missing visual responses', () => { 116 | const componentId = 'test-component-id'; 117 | const componentDescription = { 118 | gamepadIndices: { button: 0 } 119 | }; 120 | 121 | expect(() => { 122 | // eslint-disable-next-line no-unused-vars 123 | const component = new Component(componentId, componentDescription); 124 | }).toThrow(); 125 | }); 126 | 127 | test('Missing gamepadIndices', () => { 128 | const componentId = 'test-component-id'; 129 | const componentDescription = { 130 | visualResponses: {} 131 | }; 132 | 133 | expect(() => { 134 | // eslint-disable-next-line no-unused-vars 135 | const component = new Component(componentId, componentDescription); 136 | }).toThrow(); 137 | }); 138 | }); 139 | 140 | describe('Update from Gamepad', () => { 141 | test.todo('something here'); 142 | }); 143 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/__tests__/motionController.test.js: -------------------------------------------------------------------------------- 1 | import { Constants } from '../constants'; 2 | import { MotionController } from '../motionController'; 3 | 4 | const profile = { 5 | id: 'mock-one-button', 6 | layouts: { 7 | none: { 8 | components: { 9 | mockButtonComponent: { 10 | gamepadIndices: { 11 | button: 0 12 | }, 13 | visualResponses: [] 14 | } 15 | } 16 | } 17 | } 18 | }; 19 | const assetUrl = 'assetUrl string'; 20 | const mockXRInputSource = { 21 | handedness: Constants.Handedness.NONE, 22 | gamepad: { 23 | buttons: [0] 24 | } 25 | }; 26 | 27 | test('No xrInputSource', () => { 28 | expect(() => { 29 | // eslint-disable-next-line no-unused-vars 30 | const motionController = new MotionController(undefined, profile, assetUrl); 31 | }).toThrow(); 32 | 33 | expect(() => { 34 | // eslint-disable-next-line no-unused-vars 35 | const motionController = new MotionController(null, profile, assetUrl); 36 | }).toThrow(); 37 | }); 38 | 39 | test('No profile', () => { 40 | expect(() => { 41 | // eslint-disable-next-line no-unused-vars 42 | const motionController = new MotionController(mockXRInputSource, undefined, assetUrl); 43 | }).toThrow(); 44 | 45 | expect(() => { 46 | // eslint-disable-next-line no-unused-vars 47 | const motionController = new MotionController(mockXRInputSource, null, assetUrl); 48 | }).toThrow(); 49 | }); 50 | 51 | test('No gamepad', () => { 52 | const noGamepadXRInputSource = { 53 | handedness: Constants.Handedness.NONE 54 | }; 55 | expect(() => { 56 | // eslint-disable-next-line no-unused-vars 57 | const motionController = new MotionController(noGamepadXRInputSource, profile, assetUrl); 58 | }).toThrow(); 59 | }); 60 | 61 | test('Successful construction', () => { 62 | const motionController = new MotionController(mockXRInputSource, profile, assetUrl); 63 | expect(motionController).toBeDefined(); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/component.d.ts: -------------------------------------------------------------------------------- 1 | import { Constants } from './constants'; 2 | import { VisualResponse } from './visualResponse'; 3 | 4 | export class Component { 5 | constructor(componentId: string, componentDescription: object); 6 | 7 | readonly id: string; 8 | readonly type: Constants.ComponentType; 9 | readonly rootNodeName: string; 10 | readonly touchPointNodeName: string; 11 | 12 | readonly visualResponses: { [key: string]: VisualResponse }; 13 | readonly gamepadIndices: { button: number; xAxis: number; yAxis: number }; 14 | readonly values: { 15 | state: Constants.ComponentState; 16 | button?: number; 17 | xAxis?: number; 18 | yAxis?: number }; 19 | 20 | get data(): object; 21 | 22 | updateFromGamepad(gamepad: Gamepad): void; 23 | } 24 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/components.js: -------------------------------------------------------------------------------- 1 | import { Constants } from './constants'; 2 | import { VisualResponse } from './visualResponse'; 3 | 4 | class Component { 5 | /** 6 | * @param {Object} componentId - Id of the component 7 | * @param {Object} componentDescription - Description of the component to be created 8 | */ 9 | constructor(componentId, componentDescription) { 10 | if (!componentId 11 | || !componentDescription 12 | || !componentDescription.visualResponses 13 | || !componentDescription.gamepadIndices 14 | || Object.keys(componentDescription.gamepadIndices).length === 0) { 15 | throw new Error('Invalid arguments supplied'); 16 | } 17 | 18 | this.id = componentId; 19 | this.type = componentDescription.type; 20 | this.rootNodeName = componentDescription.rootNodeName; 21 | this.touchPointNodeName = componentDescription.touchPointNodeName; 22 | 23 | // Build all the visual responses for this component 24 | this.visualResponses = {}; 25 | Object.keys(componentDescription.visualResponses).forEach((responseName) => { 26 | const visualResponse = new VisualResponse(componentDescription.visualResponses[responseName]); 27 | this.visualResponses[responseName] = visualResponse; 28 | }); 29 | 30 | // Set default values 31 | this.gamepadIndices = Object.assign({}, componentDescription.gamepadIndices); 32 | 33 | this.values = { 34 | state: Constants.ComponentState.DEFAULT, 35 | button: (this.gamepadIndices.button !== undefined) ? 0 : undefined, 36 | xAxis: (this.gamepadIndices.xAxis !== undefined) ? 0 : undefined, 37 | yAxis: (this.gamepadIndices.yAxis !== undefined) ? 0 : undefined 38 | }; 39 | } 40 | 41 | get data() { 42 | const data = { id: this.id, ...this.values }; 43 | return data; 44 | } 45 | 46 | /** 47 | * @description Poll for updated data based on current gamepad state 48 | * @param {Object} gamepad - The gamepad object from which the component data should be polled 49 | */ 50 | updateFromGamepad(gamepad) { 51 | // Set the state to default before processing other data sources 52 | this.values.state = Constants.ComponentState.DEFAULT; 53 | 54 | // Get and normalize button 55 | if (this.gamepadIndices.button !== undefined 56 | && gamepad.buttons.length > this.gamepadIndices.button) { 57 | const gamepadButton = gamepad.buttons[this.gamepadIndices.button]; 58 | this.values.button = gamepadButton.value; 59 | this.values.button = (this.values.button < 0) ? 0 : this.values.button; 60 | this.values.button = (this.values.button > 1) ? 1 : this.values.button; 61 | 62 | // Set the state based on the button 63 | if (gamepadButton.pressed || this.values.button === 1) { 64 | this.values.state = Constants.ComponentState.PRESSED; 65 | } else if (gamepadButton.touched || this.values.button > Constants.ButtonTouchThreshold) { 66 | this.values.state = Constants.ComponentState.TOUCHED; 67 | } 68 | } 69 | 70 | // Get and normalize x axis value 71 | if (this.gamepadIndices.xAxis !== undefined 72 | && gamepad.axes.length > this.gamepadIndices.xAxis) { 73 | this.values.xAxis = gamepad.axes[this.gamepadIndices.xAxis]; 74 | this.values.xAxis = (this.values.xAxis < -1) ? -1 : this.values.xAxis; 75 | this.values.xAxis = (this.values.xAxis > 1) ? 1 : this.values.xAxis; 76 | 77 | // If the state is still default, check if the xAxis makes it touched 78 | if (this.values.state === Constants.ComponentState.DEFAULT 79 | && Math.abs(this.values.xAxis) > Constants.AxisTouchThreshold) { 80 | this.values.state = Constants.ComponentState.TOUCHED; 81 | } 82 | } 83 | 84 | // Get and normalize Y axis value 85 | if (this.gamepadIndices.yAxis !== undefined 86 | && gamepad.axes.length > this.gamepadIndices.yAxis) { 87 | this.values.yAxis = gamepad.axes[this.gamepadIndices.yAxis]; 88 | this.values.yAxis = (this.values.yAxis < -1) ? -1 : this.values.yAxis; 89 | this.values.yAxis = (this.values.yAxis > 1) ? 1 : this.values.yAxis; 90 | 91 | // If the state is still default, check if the yAxis makes it touched 92 | if (this.values.state === Constants.ComponentState.DEFAULT 93 | && Math.abs(this.values.yAxis) > Constants.AxisTouchThreshold) { 94 | this.values.state = Constants.ComponentState.TOUCHED; 95 | } 96 | } 97 | 98 | // Update the visual response weights based on the current component data 99 | Object.values(this.visualResponses).forEach((visualResponse) => { 100 | visualResponse.updateFromComponent(this.values); 101 | }); 102 | } 103 | } 104 | 105 | export { Component }; 106 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/constants.d.ts: -------------------------------------------------------------------------------- 1 | export namespace Constants { 2 | export const enum Handedness { 3 | NONE = 'none', 4 | LEFT = 'left', 5 | RIGHT = 'right' 6 | } 7 | 8 | export const enum ComponentState { 9 | DEFAULT = 'default', 10 | TOUCHED = 'touched', 11 | PRESSED = 'pressed' 12 | } 13 | 14 | export const enum ComponentProperty { 15 | BUTTON = 'button', 16 | X_AXIS = 'xAxis', 17 | Y_AXIS = 'yAxis', 18 | STATE = 'state' 19 | } 20 | 21 | export const enum ComponentType { 22 | TRIGGER = 'trigger', 23 | SQUEEZE = 'squeeze', 24 | TOUCHPAD = 'touchpad', 25 | THUMBSTICK = 'thumbstick', 26 | BUTTON = 'button' 27 | } 28 | 29 | export const ButtonTouchThreshold = 0.05; 30 | export const AxisTouchThreshold = 0.1; 31 | 32 | export const enum VisualResponseProperty { 33 | TRANSFORM = 'transform', 34 | VISIBILITY = 'visibility' 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/constants.js: -------------------------------------------------------------------------------- 1 | const Constants = { 2 | Handedness: /* @__PURE__ */ Object.freeze({ 3 | NONE: 'none', 4 | LEFT: 'left', 5 | RIGHT: 'right' 6 | }), 7 | 8 | ComponentState: /* @__PURE__ */ Object.freeze({ 9 | DEFAULT: 'default', 10 | TOUCHED: 'touched', 11 | PRESSED: 'pressed' 12 | }), 13 | 14 | ComponentProperty: /* @__PURE__ */ Object.freeze({ 15 | BUTTON: 'button', 16 | X_AXIS: 'xAxis', 17 | Y_AXIS: 'yAxis', 18 | STATE: 'state' 19 | }), 20 | 21 | ComponentType: /* @__PURE__ */ Object.freeze({ 22 | TRIGGER: 'trigger', 23 | SQUEEZE: 'squeeze', 24 | TOUCHPAD: 'touchpad', 25 | THUMBSTICK: 'thumbstick', 26 | BUTTON: 'button' 27 | }), 28 | 29 | ButtonTouchThreshold: 0.05, 30 | 31 | AxisTouchThreshold: 0.1, 32 | 33 | VisualResponseProperty: /* @__PURE__ */ Object.freeze({ 34 | TRANSFORM: 'transform', 35 | VISIBILITY: 'visibility' 36 | }) 37 | }; 38 | 39 | export { Constants }; 40 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export * from './profiles'; 3 | export * from './motionController'; 4 | export * from './component'; 5 | export * from './visualResponse'; 6 | 7 | export as namespace WebXRInputProfiles; 8 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/index.js: -------------------------------------------------------------------------------- 1 | export { Constants } from './constants'; 2 | export { fetchProfile, fetchProfilesList } from './profiles'; 3 | export { MotionController } from './motionController'; 4 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/motionController.d.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "./component"; 2 | 3 | export class MotionController { 4 | constructor(xrInputSource: object, profile: object, assetUrl: string); 5 | 6 | readonly xrInputSource: object; 7 | readonly assetUrl: string; 8 | readonly id: string; 9 | readonly components: { [key: string]: Component }; 10 | 11 | get data(): object; 12 | 13 | updateFromGamepad(): void; 14 | } 15 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/motionController.js: -------------------------------------------------------------------------------- 1 | import { Component } from './components'; 2 | 3 | /** 4 | * @description Builds a motion controller with components and visual responses based on the 5 | * supplied profile description. Data is polled from the xrInputSource's gamepad. 6 | * @author Nell Waliczek / https://github.com/NellWaliczek 7 | */ 8 | class MotionController { 9 | /** 10 | * @param {Object} xrInputSource - The XRInputSource to build the MotionController around 11 | * @param {Object} profile - The best matched profile description for the supplied xrInputSource 12 | * @param {Object} assetUrl 13 | */ 14 | constructor(xrInputSource, profile, assetUrl) { 15 | if (!xrInputSource) { 16 | throw new Error('No xrInputSource supplied'); 17 | } 18 | 19 | if (!profile) { 20 | throw new Error('No profile supplied'); 21 | } 22 | 23 | this.xrInputSource = xrInputSource; 24 | this.assetUrl = assetUrl; 25 | this.id = profile.profileId; 26 | 27 | // Build child components as described in the profile description 28 | this.layoutDescription = profile.layouts[xrInputSource.handedness]; 29 | this.components = {}; 30 | Object.keys(this.layoutDescription.components).forEach((componentId) => { 31 | const componentDescription = this.layoutDescription.components[componentId]; 32 | this.components[componentId] = new Component(componentId, componentDescription); 33 | }); 34 | 35 | // Initialize components based on current gamepad state 36 | this.updateFromGamepad(); 37 | } 38 | 39 | get gripSpace() { 40 | return this.xrInputSource.gripSpace; 41 | } 42 | 43 | get targetRaySpace() { 44 | return this.xrInputSource.targetRaySpace; 45 | } 46 | 47 | /** 48 | * @description Returns a subset of component data for simplified debugging 49 | */ 50 | get data() { 51 | const data = []; 52 | Object.values(this.components).forEach((component) => { 53 | data.push(component.data); 54 | }); 55 | return data; 56 | } 57 | 58 | /** 59 | * @description Poll for updated data based on current gamepad state 60 | */ 61 | updateFromGamepad() { 62 | Object.values(this.components).forEach((component) => { 63 | component.updateFromGamepad(this.xrInputSource.gamepad); 64 | }); 65 | } 66 | } 67 | 68 | export { MotionController }; 69 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/profiles.d.ts: -------------------------------------------------------------------------------- 1 | export as namespace Profiles; 2 | 3 | export function fetchProfilesList(basePath: string): Promise<{ [key: string]: string }>; 4 | export function fetchProfile( 5 | xrInputSource: object, 6 | basePath: string, 7 | defaultProfileId?: string, 8 | getAssetPath?: boolean 9 | ): Promise<{ profile: object; assetPath?: string }>; 10 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/profiles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Static helper function to fetch a JSON file and turn it into a JS object 3 | * @param {string} path - Path to JSON file to be fetched 4 | */ 5 | async function fetchJsonFile(path) { 6 | const response = await fetch(path); 7 | if (!response.ok) { 8 | throw new Error(response.statusText); 9 | } else { 10 | return response.json(); 11 | } 12 | } 13 | 14 | async function fetchProfilesList(basePath) { 15 | if (!basePath) { 16 | throw new Error('No basePath supplied'); 17 | } 18 | 19 | const profileListFileName = 'profilesList.json'; 20 | const profilesList = await fetchJsonFile(`${basePath}/${profileListFileName}`); 21 | return profilesList; 22 | } 23 | 24 | async function fetchProfile(xrInputSource, basePath, defaultProfile = null, getAssetPath = true) { 25 | if (!xrInputSource) { 26 | throw new Error('No xrInputSource supplied'); 27 | } 28 | 29 | if (!basePath) { 30 | throw new Error('No basePath supplied'); 31 | } 32 | 33 | // Get the list of profiles 34 | const supportedProfilesList = await fetchProfilesList(basePath); 35 | 36 | // Find the relative path to the first requested profile that is recognized 37 | let match; 38 | xrInputSource.profiles.some((profileId) => { 39 | const supportedProfile = supportedProfilesList[profileId]; 40 | if (supportedProfile) { 41 | match = { 42 | profileId, 43 | profilePath: `${basePath}/${supportedProfile.path}`, 44 | deprecated: !!supportedProfile.deprecated 45 | }; 46 | } 47 | return !!match; 48 | }); 49 | 50 | if (!match) { 51 | if (!defaultProfile) { 52 | throw new Error('No matching profile name found'); 53 | } 54 | 55 | const supportedProfile = supportedProfilesList[defaultProfile]; 56 | if (!supportedProfile) { 57 | throw new Error(`No matching profile name found and default profile "${defaultProfile}" missing.`); 58 | } 59 | 60 | match = { 61 | profileId: defaultProfile, 62 | profilePath: `${basePath}/${supportedProfile.path}`, 63 | deprecated: !!supportedProfile.deprecated 64 | }; 65 | } 66 | 67 | const profile = await fetchJsonFile(match.profilePath); 68 | 69 | let assetPath; 70 | if (getAssetPath) { 71 | let layout; 72 | if (xrInputSource.handedness === 'any') { 73 | layout = profile.layouts[Object.keys(profile.layouts)[0]]; 74 | } else { 75 | layout = profile.layouts[xrInputSource.handedness]; 76 | } 77 | if (!layout) { 78 | throw new Error( 79 | `No matching handedness, ${xrInputSource.handedness}, in profile ${match.profileId}` 80 | ); 81 | } 82 | 83 | if (layout.assetPath) { 84 | assetPath = match.profilePath.replace('profile.json', layout.assetPath); 85 | } 86 | } 87 | 88 | return { profile, assetPath }; 89 | } 90 | 91 | export { fetchProfilesList, fetchProfile }; 92 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/visualResponse.d.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component'; 2 | import { Constants } from './constants'; 3 | 4 | export class VisualResponse { 5 | constructor(visualResponseDescription: object); 6 | 7 | readonly componentProperty: Constants.ComponentProperty; 8 | readonly states: [Constants.ComponentState]; 9 | readonly valueNodeName: string; 10 | readonly valueNodeProperty: Constants.VisualResponseProperty; 11 | 12 | readonly minNodeName?: string; 13 | readonly maxNodeName?: string; 14 | 15 | readonly value: number | boolean; 16 | 17 | updateFromComponent(component: { 18 | state: Constants.ComponentState; 19 | button?: number; 20 | xAxis?: number; 21 | yAxis?: number 22 | }): void; 23 | } 24 | -------------------------------------------------------------------------------- /packages/motion-controllers/src/visualResponse.js: -------------------------------------------------------------------------------- 1 | import { Constants } from './constants'; 2 | 3 | /** @constant {Object} */ 4 | const defaultComponentValues = /* @__PURE__ */ (() => ({ 5 | xAxis: 0, 6 | yAxis: 0, 7 | button: 0, 8 | state: Constants.ComponentState.DEFAULT 9 | }))(); 10 | 11 | /** 12 | * @description Converts an X, Y coordinate from the range -1 to 1 (as reported by the Gamepad 13 | * API) to the range 0 to 1 (for interpolation). Also caps the X, Y values to be bounded within 14 | * a circle. This ensures that thumbsticks are not animated outside the bounds of their physical 15 | * range of motion and touchpads do not report touch locations off their physical bounds. 16 | * @param {number} x The original x coordinate in the range -1 to 1 17 | * @param {number} y The original y coordinate in the range -1 to 1 18 | */ 19 | function normalizeAxes(x = 0, y = 0) { 20 | let xAxis = x; 21 | let yAxis = y; 22 | 23 | // Determine if the point is outside the bounds of the circle 24 | // and, if so, place it on the edge of the circle 25 | const hypotenuse = Math.sqrt((x * x) + (y * y)); 26 | if (hypotenuse > 1) { 27 | const theta = Math.atan2(y, x); 28 | xAxis = Math.cos(theta); 29 | yAxis = Math.sin(theta); 30 | } 31 | 32 | // Scale and move the circle so values are in the interpolation range. The circle's origin moves 33 | // from (0, 0) to (0.5, 0.5). The circle's radius scales from 1 to be 0.5. 34 | const result = { 35 | normalizedXAxis: (xAxis * 0.5) + 0.5, 36 | normalizedYAxis: (yAxis * 0.5) + 0.5 37 | }; 38 | return result; 39 | } 40 | 41 | /** 42 | * Contains the description of how the 3D model should visually respond to a specific user input. 43 | * This is accomplished by initializing the object with the name of a node in the 3D model and 44 | * property that need to be modified in response to user input, the name of the nodes representing 45 | * the allowable range of motion, and the name of the input which triggers the change. In response 46 | * to the named input changing, this object computes the appropriate weighting to use for 47 | * interpolating between the range of motion nodes. 48 | */ 49 | class VisualResponse { 50 | constructor(visualResponseDescription) { 51 | this.componentProperty = visualResponseDescription.componentProperty; 52 | this.states = visualResponseDescription.states; 53 | this.valueNodeName = visualResponseDescription.valueNodeName; 54 | this.valueNodeProperty = visualResponseDescription.valueNodeProperty; 55 | 56 | if (this.valueNodeProperty === Constants.VisualResponseProperty.TRANSFORM) { 57 | this.minNodeName = visualResponseDescription.minNodeName; 58 | this.maxNodeName = visualResponseDescription.maxNodeName; 59 | } 60 | 61 | // Initializes the response's current value based on default data 62 | this.value = 0; 63 | this.updateFromComponent(defaultComponentValues); 64 | } 65 | 66 | /** 67 | * Computes the visual response's interpolation weight based on component state 68 | * @param {Object} componentValues - The component from which to update 69 | * @param {number} xAxis - The reported X axis value of the component 70 | * @param {number} yAxis - The reported Y axis value of the component 71 | * @param {number} button - The reported value of the component's button 72 | * @param {string} state - The component's active state 73 | */ 74 | updateFromComponent({ 75 | xAxis, yAxis, button, state 76 | }) { 77 | const { normalizedXAxis, normalizedYAxis } = normalizeAxes(xAxis, yAxis); 78 | switch (this.componentProperty) { 79 | case Constants.ComponentProperty.X_AXIS: 80 | this.value = (this.states.includes(state)) ? normalizedXAxis : 0.5; 81 | break; 82 | case Constants.ComponentProperty.Y_AXIS: 83 | this.value = (this.states.includes(state)) ? normalizedYAxis : 0.5; 84 | break; 85 | case Constants.ComponentProperty.BUTTON: 86 | this.value = (this.states.includes(state)) ? button : 0; 87 | break; 88 | case Constants.ComponentProperty.STATE: 89 | if (this.valueNodeProperty === Constants.VisualResponseProperty.VISIBILITY) { 90 | this.value = (this.states.includes(state)); 91 | } else { 92 | this.value = this.states.includes(state) ? 1.0 : 0.0; 93 | } 94 | break; 95 | default: 96 | throw new Error(`Unexpected visualResponse componentProperty ${this.componentProperty}`); 97 | } 98 | } 99 | } 100 | 101 | export { VisualResponse }; 102 | -------------------------------------------------------------------------------- /packages/motion-controllers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "allowJs": true, 5 | "checkJs": true, 6 | "target": "ES5" 7 | }, 8 | "include": ["./src/*.d.ts", "./src/__tests__/*.js"], 9 | } 10 | -------------------------------------------------------------------------------- /packages/registry/LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). -------------------------------------------------------------------------------- /packages/registry/gulpTasks/copySchemasTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const path = require('path'); 3 | const jsonCombine = require('gulp-jsoncombine'); 4 | 5 | const taskPaths = require('./taskPaths'); 6 | 7 | const mainSchemaFilename = 'profile.schema.json'; 8 | 9 | function combineSchemas(input) { 10 | const mainSchemaKey = path.basename(mainSchemaFilename, '.json'); 11 | const mainSchema = input[mainSchemaKey]; 12 | const dependencies = []; 13 | Object.keys(input).forEach((key) => { 14 | if (key !== mainSchemaKey) { 15 | dependencies.push(input[key]); 16 | } 17 | }); 18 | 19 | const schemas = { 20 | mainSchema, 21 | dependencies 22 | }; 23 | return Buffer.from(JSON.stringify(schemas, null, 2)); 24 | } 25 | 26 | function copySchemas() { 27 | const srcGlob = taskPaths.schemasGlob; 28 | return gulp.src(srcGlob) 29 | .pipe(gulp.dest(taskPaths.schemasDest)) 30 | .pipe(jsonCombine(taskPaths.schemasCombinedFilename, combineSchemas)) 31 | .pipe(gulp.dest(taskPaths.toolsDest)); 32 | } 33 | 34 | module.exports = copySchemas; 35 | -------------------------------------------------------------------------------- /packages/registry/gulpTasks/copyToolsTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const through = require('through2'); 3 | const path = require('path'); 4 | 5 | const taskPaths = require('./taskPaths'); 6 | 7 | function convertMergeScript() { 8 | return through.obj((vinylFile, encoding, callback) => { 9 | const newFile = vinylFile.clone(); 10 | if (path.extname(vinylFile.relative) === '.js') { 11 | let fileContents = vinylFile.contents.toString(); 12 | fileContents = fileContents.replace('module.exports =', 'export default'); 13 | newFile.contents = Buffer.from(fileContents); 14 | } 15 | callback(null, newFile); 16 | }); 17 | } 18 | 19 | function copyProfilesTools() { 20 | return gulp.src(taskPaths.toolsGlob) 21 | .pipe(convertMergeScript()) 22 | .pipe(gulp.dest(taskPaths.toolsDest)); 23 | } 24 | 25 | module.exports = copyProfilesTools; 26 | -------------------------------------------------------------------------------- /packages/registry/gulpTasks/taskPaths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const taskPaths = {}; 4 | 5 | taskPaths.dest = path.join(__dirname, '../dist'); 6 | 7 | taskPaths.profilesSrc = path.join(__dirname, '../profiles'); 8 | taskPaths.profilesGlob = path.join(taskPaths.profilesSrc, '**/*.json'); 9 | taskPaths.profilesDest = path.join(taskPaths.dest, 'profiles'); 10 | 11 | taskPaths.profilesListDest = path.join(taskPaths.dest, 'profilesList.json'); 12 | 13 | taskPaths.schemasSrc = path.join(__dirname, '../schemas'); 14 | taskPaths.schemasGlob = path.join(taskPaths.schemasSrc, '**'); 15 | taskPaths.schemasDest = path.join(taskPaths.dest, 'schemas'); 16 | taskPaths.schemasCombinedFilename = 'registrySchemas.json'; 17 | 18 | taskPaths.toolsSrc = path.join(__dirname, '../src'); 19 | taskPaths.toolsGlob = path.join(taskPaths.toolsSrc, '*.js'); 20 | taskPaths.toolsDest = path.join(taskPaths.dest, 'profilesTools'); 21 | 22 | taskPaths.watchGlobs = [taskPaths.profilesGlob, taskPaths.schemasGlob]; 23 | 24 | module.exports = taskPaths; 25 | -------------------------------------------------------------------------------- /packages/registry/gulpTasks/validateProfilesTask.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const path = require('path'); 3 | const fs = require('fs-extra'); 4 | const Ajv = require('ajv'); 5 | const through = require('through2'); 6 | 7 | const taskPaths = require('./taskPaths'); 8 | const validateProfile = require('../src/validateRegistryProfile'); 9 | 10 | /** 11 | * Validate the profile against the schema 12 | * @param {object} profileJson - the profile to validate 13 | */ 14 | function buildSchemaValidator() { 15 | const ajv = new Ajv(); 16 | const schemasPath = path.join(taskPaths.toolsDest, taskPaths.schemasCombinedFilename); 17 | const schemas = fs.readJsonSync(schemasPath); 18 | schemas.dependencies.forEach((schema) => { 19 | ajv.addSchema(schema); 20 | }); 21 | return ajv.compile(schemas.mainSchema); 22 | } 23 | 24 | function validate(schemaValidator) { 25 | return through.obj((vinylFile, encoding, callback) => { 26 | const profileJson = JSON.parse(vinylFile.contents.toString()); 27 | 28 | // Validate the supplied profile conforms to schema 29 | if (!schemaValidator(profileJson)) { 30 | const errors = JSON.stringify(schemaValidator.errors, null, 2); 31 | throw new Error(`${profileJson.profileId} Failed to validate schema with errors: ${errors}`); 32 | } 33 | 34 | validateProfile(profileJson); 35 | 36 | callback(null, vinylFile); 37 | }); 38 | } 39 | 40 | function mergeProfiles() { 41 | const schemaValidator = buildSchemaValidator(); 42 | 43 | return gulp.src(taskPaths.profilesGlob) 44 | .pipe(validate(schemaValidator)) 45 | .pipe(gulp.dest(taskPaths.profilesDest)); 46 | } 47 | 48 | module.exports = mergeProfiles; 49 | -------------------------------------------------------------------------------- /packages/registry/gulpTasks/writeProfilesListTask.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const glob = require('glob'); 4 | 5 | const taskPaths = require('./taskPaths'); 6 | 7 | function generate() { 8 | return new Promise((resolve, reject) => { 9 | const profilesList = {}; 10 | glob(taskPaths.profilesGlob, null, (error, files) => { 11 | if (error) { 12 | reject(error); 13 | } else { 14 | files.forEach((file) => { 15 | const profileId = path.basename(file, '.json'); 16 | const relativePath = file.substr((taskPaths.profilesSrc.length) + 1); 17 | profilesList[profileId] = { path: relativePath }; 18 | 19 | // If there are any deprecated profile ids listed in the file, list 20 | // them as pointing to the same path as the standard profile id. 21 | const profileJson = fs.readJsonSync(file); 22 | if (profileJson.deprecatedProfileIds) { 23 | profileJson.deprecatedProfileIds.forEach((deprecatedId) => { 24 | profilesList[deprecatedId] = { 25 | path: relativePath, 26 | deprecated: true 27 | }; 28 | }); 29 | } 30 | }); 31 | 32 | resolve(profilesList); 33 | } 34 | }); 35 | }); 36 | } 37 | 38 | function writeProfilesList() { 39 | return generate().then((profilesList) => { 40 | fs.outputJsonSync(taskPaths.profilesListDest, profilesList, { spaces: 2 }); 41 | }); 42 | } 43 | 44 | module.exports = writeProfilesList; 45 | -------------------------------------------------------------------------------- /packages/registry/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const fs = require('fs-extra'); 3 | 4 | const taskPaths = require('./gulpTasks/taskPaths'); 5 | const validateProfiles = require('./gulpTasks/validateProfilesTask'); 6 | const copySchemas = require('./gulpTasks/copySchemasTask'); 7 | const copyTools = require('./gulpTasks/copyToolsTask'); 8 | const writeProfilesList = require('./gulpTasks/writeProfilesListTask'); 9 | 10 | function clean() { 11 | return fs.remove(taskPaths.dest); 12 | } 13 | 14 | const build = gulp.series( 15 | copySchemas, 16 | validateProfiles, 17 | gulp.parallel(copyTools, writeProfilesList) 18 | ); 19 | 20 | const cleanBuild = gulp.series(clean, build); 21 | 22 | function watchBuild() { 23 | return gulp.watch(taskPaths.watchGlobs, { ignoreInitial: false }, cleanBuild); 24 | } 25 | 26 | exports.clean = clean; 27 | exports.build = build; 28 | exports.cleanBuild = cleanBuild; 29 | exports.watch = watchBuild; 30 | 31 | exports.default = cleanBuild; 32 | -------------------------------------------------------------------------------- /packages/registry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webxr-input-profiles/registry", 3 | "version": "1.0.4", 4 | "description": "Registry for profile ids and their associated button layouts", 5 | "main": "dist/profilesList.json", 6 | "files": [ 7 | "package.json", 8 | "LICENSE", 9 | "README.md", 10 | "dist/profiles/", 11 | "schemas/", 12 | "src/**" 13 | ], 14 | "scripts": { 15 | "clean": "gulp clean", 16 | "build": "gulp", 17 | "cleanBuild": "gulp cleanBuild", 18 | "watch": "gulp watch", 19 | "test": "echo \"Run tests from root\" && exit 1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-button.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-button", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "face-button", 7 | "components": { 8 | "face-button": { "type": "button" } 9 | }, 10 | "gamepad": { 11 | "mapping": "", 12 | "buttons": [ 13 | "face-button" 14 | ], 15 | "axes":[] 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-fixed-hand.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-fixed-hand", 3 | "fallbackProfileIds": ["generic-hand-select", "generic-trigger"], 4 | "layouts" : { 5 | "left" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" } 9 | }, 10 | "gamepad": { 11 | "mapping": "xr-standard", 12 | "buttons": [ 13 | "xr-standard-trigger" 14 | ], 15 | "axes":[] 16 | } 17 | }, 18 | "right" : { 19 | "selectComponentId": "xr-standard-trigger", 20 | "components": { 21 | "xr-standard-trigger": { "type": "trigger" } 22 | }, 23 | "gamepad": { 24 | "mapping": "xr-standard", 25 | "buttons": [ 26 | "xr-standard-trigger" 27 | ], 28 | "axes":[] 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-hand-select-grasp.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-hand-select-grasp", 3 | "fallbackProfileIds": ["generic-hand-select"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "grasp": { "type": "trigger" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | null, 17 | null, 18 | "grasp" 19 | ], 20 | "axes":[] 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-hand-select.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-hand-select", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" } 9 | }, 10 | "gamepad": { 11 | "mapping": "xr-standard", 12 | "buttons": [ 13 | "xr-standard-trigger" 14 | ], 15 | "axes":[] 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-hand.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-hand", 3 | "fallbackProfileIds": ["generic-hand-select"], 4 | "layouts" : { 5 | "left" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" } 9 | }, 10 | "gamepad": { 11 | "mapping": "xr-standard", 12 | "buttons": [ 13 | "xr-standard-trigger" 14 | ], 15 | "axes":[] 16 | } 17 | }, 18 | "right" : { 19 | "selectComponentId": "xr-standard-trigger", 20 | "components": { 21 | "xr-standard-trigger": { "type": "trigger" } 22 | }, 23 | "gamepad": { 24 | "mapping": "xr-standard", 25 | "buttons": [ 26 | "xr-standard-trigger" 27 | ], 28 | "axes":[] 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-touchpad.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-touchpad", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "touchpad", 7 | "components": { 8 | "touchpad": { "type": "touchpad" } 9 | }, 10 | "gamepad": { 11 | "mapping": "", 12 | "buttons": [ 13 | null, 14 | null, 15 | "touchpad" 16 | ], 17 | "axes":[ 18 | { "componentId": "touchpad", "axis": "x-axis"}, 19 | { "componentId": "touchpad", "axis": "y-axis"} 20 | ] 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-touchscreen.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-touchscreen", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "none" : { 6 | "selectComponentId": "touchscreen", 7 | "components": { 8 | "touchscreen": { "type": "touchpad" } 9 | }, 10 | "gamepad": { 11 | "mapping": "", 12 | "buttons": [ 13 | null, 14 | null, 15 | "touchscreen" 16 | ], 17 | "axes":[ 18 | { "componentId": "touchscreen", "axis": "x-axis"}, 19 | { "componentId": "touchscreen", "axis": "y-axis"} 20 | ] 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-squeeze-thumbstick.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-squeeze-thumbstick", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" } 11 | }, 12 | "gamepad":{ 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | "xr-standard-squeeze", 17 | null, 18 | "xr-standard-thumbstick" 19 | ], 20 | "axes":[ 21 | null, 22 | null, 23 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 24 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 25 | ] 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-squeeze-touchpad-thumbstick.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-squeeze-touchpad-thumbstick", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" }, 11 | "xr-standard-thumbstick": { "type": "thumbstick" } 12 | }, 13 | "gamepad": { 14 | "mapping": "xr-standard", 15 | "buttons": [ 16 | "xr-standard-trigger", 17 | "xr-standard-squeeze", 18 | "xr-standard-touchpad", 19 | "xr-standard-thumbstick" 20 | ], 21 | "axes":[ 22 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"}, 24 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 25 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 26 | ] 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-squeeze-touchpad.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-squeeze-touchpad", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" } 11 | }, 12 | "gamepad": { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | "xr-standard-squeeze", 17 | "xr-standard-touchpad" 18 | ], 19 | "axes":[ 20 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 21 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 22 | ] 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-squeeze.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger-squeeze", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | "xr-standard-squeeze" 16 | ], 17 | "axes":[] 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-thumbstick.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-thumbstick", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-thumbstick": { "type": "thumbstick" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | null, 17 | "xr-standard-thumbstick" 18 | ], 19 | "axes":[ 20 | null, 21 | null, 22 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 24 | ] 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-touchpad-thumbstick.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-touchpad-thumbstick", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" } 11 | }, 12 | "gamepad" : { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | null, 17 | "xr-standard-touchpad", 18 | "xr-standard-thumbstick" 19 | ], 20 | "axes":[ 21 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 22 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"}, 23 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 24 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 25 | ] 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger-touchpad.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "generic-trigger-touchpad", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" } 10 | }, 11 | "gamepad" : { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | "xr-standard-touchpad" 17 | ], 18 | "axes":[ 19 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 20 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 21 | ] 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /packages/registry/profiles/generic/generic-trigger.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "generic-trigger", 3 | "fallbackProfileIds": [], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" } 9 | }, 10 | "gamepad": { 11 | "mapping": "xr-standard", 12 | "buttons": [ 13 | "xr-standard-trigger" 14 | ], 15 | "axes":[] 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /packages/registry/profiles/google/google-daydream.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "google-daydream", 3 | "fallbackProfileIds": ["generic-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "touchpad", 7 | "components": { 8 | "touchpad": { "type": "touchpad" } 9 | }, 10 | "gamepad": { 11 | "mapping": "", 12 | "buttons": [ 13 | "touchpad" 14 | ], 15 | "axes":[ 16 | { "componentId": "touchpad", "axis": "x-axis"}, 17 | { "componentId": "touchpad", "axis": "y-axis"} 18 | ] 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/registry/profiles/hp/hp-mixed-reality.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "hp-mixed-reality", 3 | "fallbackProfileIds": [ "oculus-touch", "generic-trigger-squeeze-thumbstick"], 4 | "layouts" : { 5 | "left" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad" : { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right" : { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" }, 41 | "menu" : { "type": "button", "reserved": true } 42 | }, 43 | "gamepad" : { 44 | "mapping": "xr-standard", 45 | "buttons": [ 46 | "xr-standard-trigger", 47 | "xr-standard-squeeze", 48 | null, 49 | "xr-standard-thumbstick", 50 | "a-button", 51 | "b-button" 52 | ], 53 | "axes":[ 54 | null, 55 | null, 56 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 57 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 58 | ] 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/registry/profiles/htc/htc-vive-cosmos.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "htc-vive-cosmos", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-thumbstick" ], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "bumper" : { "type": "button" } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button", 24 | "bumper" 25 | ], 26 | "axes":[ 27 | null, 28 | null, 29 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 30 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 31 | ] 32 | } 33 | }, 34 | "right": { 35 | "selectComponentId": "xr-standard-trigger", 36 | "components": { 37 | "xr-standard-trigger": { "type": "trigger" }, 38 | "xr-standard-squeeze": { "type": "squeeze" }, 39 | "xr-standard-thumbstick": { "type": "thumbstick" }, 40 | "a-button" : { "type": "button" }, 41 | "b-button" : { "type": "button" }, 42 | "bumper" : { "type": "button" } 43 | }, 44 | "gamepad": { 45 | "mapping": "xr-standard", 46 | "buttons": [ 47 | "xr-standard-trigger", 48 | "xr-standard-squeeze", 49 | null, 50 | "xr-standard-thumbstick", 51 | "a-button", 52 | "b-button", 53 | "bumper" 54 | ], 55 | "axes":[ 56 | null, 57 | null, 58 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 59 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 60 | ] 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /packages/registry/profiles/htc/htc-vive-focus-3.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "htc-vive-focus-3", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-thumbstick"], 4 | "layouts" : { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" } 41 | }, 42 | "gamepad": { 43 | "mapping": "xr-standard", 44 | "buttons": [ 45 | "xr-standard-trigger", 46 | "xr-standard-squeeze", 47 | null, 48 | "xr-standard-thumbstick", 49 | "a-button", 50 | "b-button" 51 | ], 52 | "axes":[ 53 | null, 54 | null, 55 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 56 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 57 | ] 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /packages/registry/profiles/htc/htc-vive-focus-plus.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "htc-vive-focus-plus", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" }, 11 | "menu" : { "type": "button", "reserved": true } 12 | }, 13 | "gamepad" : { 14 | "mapping": "xr-standard", 15 | "buttons": [ 16 | "xr-standard-trigger", 17 | "xr-standard-squeeze", 18 | "xr-standard-touchpad", 19 | null 20 | ], 21 | "axes":[ 22 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 24 | ] 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /packages/registry/profiles/htc/htc-vive-focus.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "htc-vive-focus", 3 | "fallbackProfileIds": [ "generic-trigger-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" }, 10 | "menu" : { "type": "button" } 11 | }, 12 | "gamepad" : { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | null, 17 | "xr-standard-touchpad", 18 | null, 19 | "menu" 20 | ], 21 | "axes":[ 22 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 24 | ] 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /packages/registry/profiles/htc/htc-vive.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "htc-vive", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" }, 11 | "menu" : { "type": "button", "reserved": true } 12 | }, 13 | "gamepad" : { 14 | "mapping": "xr-standard", 15 | "buttons": [ 16 | "xr-standard-trigger", 17 | "xr-standard-squeeze", 18 | "xr-standard-touchpad", 19 | null 20 | ], 21 | "axes":[ 22 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 24 | ] 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/registry/profiles/logitech/logitech-mx-ink.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "logitech-mx-ink", 3 | "fallbackProfileIds": [ 4 | "generic-trigger" 5 | ], 6 | "layouts": { 7 | "left-right-none": { 8 | "selectComponentId": "xr-standard-trigger", 9 | "components": { 10 | "xr-standard-trigger": { "type": "trigger" }, 11 | "xr-standard-squeeze": { "type": "squeeze" }, 12 | "touch-pad": { "type": "button" }, 13 | "tip-force": { "type": "button" }, 14 | "docked": { "type": "button" } 15 | }, 16 | "gamepad": { 17 | "mapping": "xr-standard", 18 | "buttons": [ 19 | "xr-standard-trigger", 20 | "xr-standard-squeeze", 21 | null, 22 | null, 23 | "touch-pad", 24 | "tip-force", 25 | "docked" 26 | ], 27 | "axes": [] 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/registry/profiles/magicleap/magicleap-one.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "magicleap-one", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" } 11 | }, 12 | "gamepad": { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | "xr-standard-squeeze", 17 | "xr-standard-touchpad" 18 | ], 19 | "axes":[ 20 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 21 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 22 | ] 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /packages/registry/profiles/magicleap/magicleap-two.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "magicleap-two", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" } 11 | }, 12 | "gamepad": { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | "xr-standard-squeeze", 17 | "xr-standard-touchpad" 18 | ], 19 | "axes":[ 20 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 21 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 22 | ] 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/registry/profiles/meta/meta-fixed-hand.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "meta-fixed-hand", 3 | "fallbackProfileIds": ["generic-fixed-hand", "generic-hand-select", "generic-trigger"], 4 | "layouts" : { 5 | "left" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "menu": { "type": "button" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | null, 17 | null, 18 | "menu" 19 | ], 20 | "axes":[] 21 | } 22 | }, 23 | "right" : { 24 | "selectComponentId": "xr-standard-trigger", 25 | "components": { 26 | "xr-standard-trigger": { "type": "trigger" } 27 | }, 28 | "gamepad": { 29 | "mapping": "xr-standard", 30 | "buttons": [ 31 | "xr-standard-trigger" 32 | ], 33 | "axes":[] 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/registry/profiles/meta/meta-quest-touch-plus.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "meta-quest-touch-plus", 3 | "fallbackProfileIds": [ 4 | "oculus-touch-v3", 5 | "oculus-touch", 6 | "generic-trigger-squeeze-thumbstick" 7 | ], 8 | "layouts": { 9 | "left": { 10 | "selectComponentId": "xr-standard-trigger", 11 | "components": { 12 | "xr-standard-trigger": { "type": "trigger" }, 13 | "xr-standard-squeeze": { "type": "squeeze" }, 14 | "xr-standard-thumbstick": { "type": "thumbstick" }, 15 | "x-button": { "type": "button" }, 16 | "y-button": { "type": "button" }, 17 | "thumbrest": { "type": "button" }, 18 | "menu": { "type": "button" } 19 | }, 20 | "gamepad": { 21 | "mapping": "xr-standard", 22 | "buttons": [ 23 | "xr-standard-trigger", 24 | "xr-standard-squeeze", 25 | null, 26 | "xr-standard-thumbstick", 27 | "x-button", 28 | "y-button", 29 | "thumbrest", 30 | "menu" 31 | ], 32 | "axes": [ 33 | null, 34 | null, 35 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis" }, 36 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis" } 37 | ] 38 | } 39 | }, 40 | "right": { 41 | "selectComponentId": "xr-standard-trigger", 42 | "components": { 43 | "xr-standard-trigger": { "type": "trigger" }, 44 | "xr-standard-squeeze": { "type": "squeeze" }, 45 | "xr-standard-thumbstick": { "type": "thumbstick" }, 46 | "a-button": { "type": "button" }, 47 | "b-button": { "type": "button" }, 48 | "thumbrest": { "type": "button" } 49 | }, 50 | "gamepad": { 51 | "mapping": "xr-standard", 52 | "buttons": [ 53 | "xr-standard-trigger", 54 | "xr-standard-squeeze", 55 | null, 56 | "xr-standard-thumbstick", 57 | "a-button", 58 | "b-button", 59 | "thumbrest" 60 | ], 61 | "axes": [ 62 | null, 63 | null, 64 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis" }, 65 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis" } 66 | ] 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/registry/profiles/meta/meta-quest-touch-pro.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "meta-quest-touch-pro", 3 | "fallbackProfileIds": [ 4 | "oculus-touch-v2", 5 | "oculus-touch", 6 | "generic-trigger-squeeze-thumbstick" 7 | ], 8 | "layouts": { 9 | "left": { 10 | "selectComponentId": "xr-standard-trigger", 11 | "components": { 12 | "xr-standard-trigger": { "type": "trigger" }, 13 | "xr-standard-squeeze": { "type": "squeeze" }, 14 | "xr-standard-thumbstick": { "type": "thumbstick" }, 15 | "x-button": { "type": "button" }, 16 | "y-button": { "type": "button" }, 17 | "thumbrest": { "type": "button" }, 18 | "menu": { "type": "button" } 19 | }, 20 | "gamepad": { 21 | "mapping": "xr-standard", 22 | "buttons": [ 23 | "xr-standard-trigger", 24 | "xr-standard-squeeze", 25 | null, 26 | "xr-standard-thumbstick", 27 | "x-button", 28 | "y-button", 29 | "thumbrest", 30 | "menu" 31 | ], 32 | "axes": [ 33 | null, 34 | null, 35 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis" }, 36 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis" } 37 | ] 38 | } 39 | }, 40 | "right": { 41 | "selectComponentId": "xr-standard-trigger", 42 | "components": { 43 | "xr-standard-trigger": { "type": "trigger" }, 44 | "xr-standard-squeeze": { "type": "squeeze" }, 45 | "xr-standard-thumbstick": { "type": "thumbstick" }, 46 | "a-button": { "type": "button" }, 47 | "b-button": { "type": "button" }, 48 | "thumbrest": { "type": "button" } 49 | }, 50 | "gamepad": { 51 | "mapping": "xr-standard", 52 | "buttons": [ 53 | "xr-standard-trigger", 54 | "xr-standard-squeeze", 55 | null, 56 | "xr-standard-thumbstick", 57 | "a-button", 58 | "b-button", 59 | "thumbrest" 60 | ], 61 | "axes": [ 62 | null, 63 | null, 64 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis" }, 65 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis" } 66 | ] 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/registry/profiles/microsoft/microsoft-mixed-reality.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "microsoft-mixed-reality", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-touchpad-thumbstick"], 4 | "deprecatedProfileIds": [ "windows-mixed-reality" ], 5 | "layouts" : { 6 | "left-right" : { 7 | "selectComponentId": "xr-standard-trigger", 8 | "components": { 9 | "xr-standard-trigger": { "type": "trigger" }, 10 | "xr-standard-squeeze": { "type": "squeeze" }, 11 | "xr-standard-touchpad": { "type": "touchpad" }, 12 | "xr-standard-thumbstick": { "type": "thumbstick" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad" : { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | "xr-standard-touchpad", 21 | "xr-standard-thumbstick" 22 | ], 23 | "axes":[ 24 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 25 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"}, 26 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 27 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 28 | ] 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/registry/profiles/oculus/oculus-go.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "oculus-go", 3 | "fallbackProfileIds": [ "generic-trigger-touchpad"], 4 | "layouts": { 5 | "left-right-none": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | "xr-standard-touchpad" 17 | ], 18 | "axes":[ 19 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 20 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 21 | ] 22 | 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /packages/registry/profiles/oculus/oculus-hand.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId" : "oculus-hand", 3 | "fallbackProfileIds": [ 4 | "generic-hand", 5 | "generic-hand-select" 6 | ], 7 | "layouts" : { 8 | "left" : { 9 | "selectComponentId": "xr-standard-trigger", 10 | "components": { 11 | "xr-standard-trigger": { "type": "trigger" }, 12 | "menu": { "type": "button" }, 13 | "swipe-left": { "type": "button" }, 14 | "swipe-right": { "type": "button" }, 15 | "swipe-forward": { "type": "button" }, 16 | "swipe-backward": { "type": "button" }, 17 | "tap-thumb": { "type": "button" } 18 | }, 19 | "gamepad": { 20 | "mapping": "xr-standard", 21 | "buttons": [ 22 | "xr-standard-trigger", 23 | null, 24 | null, 25 | null, 26 | "menu", 27 | "swipe-left", 28 | "swipe-right", 29 | "swipe-forward", 30 | "swipe-backward", 31 | "tap-thumb" 32 | ], 33 | "axes":[] 34 | } 35 | }, 36 | "right" : { 37 | "selectComponentId": "xr-standard-trigger", 38 | "components": { 39 | "xr-standard-trigger": { "type": "trigger" }, 40 | "swipe-left": { "type": "button" }, 41 | "swipe-right": { "type": "button" }, 42 | "swipe-forward": { "type": "button" }, 43 | "swipe-backward": { "type": "button" }, 44 | "tap-thumb": { "type": "button" } 45 | }, 46 | "gamepad": { 47 | "mapping": "xr-standard", 48 | "buttons": [ 49 | "xr-standard-trigger", 50 | null, 51 | null, 52 | null, 53 | null, 54 | "swipe-left", 55 | "swipe-right", 56 | "swipe-forward", 57 | "swipe-backward", 58 | "tap-thumb" 59 | ], 60 | "axes":[] 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /packages/registry/profiles/oculus/oculus-touch-v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "oculus-touch-v2", 3 | "fallbackProfileIds": [ "oculus-touch", "generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "thumbrest" : { "type": "button" }, 14 | "menu" : { "type": "button", "reserved": true } 15 | }, 16 | "gamepad": { 17 | "mapping": "xr-standard", 18 | "buttons": [ 19 | "xr-standard-trigger", 20 | "xr-standard-squeeze", 21 | null, 22 | "xr-standard-thumbstick", 23 | "x-button", 24 | "y-button", 25 | "thumbrest" 26 | ], 27 | "axes":[ 28 | null, 29 | null, 30 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 31 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 32 | ] 33 | } 34 | }, 35 | "right": { 36 | "selectComponentId": "xr-standard-trigger", 37 | "components": { 38 | "xr-standard-trigger": { "type": "trigger" }, 39 | "xr-standard-squeeze": { "type": "squeeze" }, 40 | "xr-standard-thumbstick": { "type": "thumbstick" }, 41 | "a-button" : { "type": "button" }, 42 | "b-button" : { "type": "button" }, 43 | "thumbrest" : { "type": "button" } 44 | }, 45 | "gamepad": { 46 | "mapping": "xr-standard", 47 | "buttons": [ 48 | "xr-standard-trigger", 49 | "xr-standard-squeeze", 50 | null, 51 | "xr-standard-thumbstick", 52 | "a-button", 53 | "b-button", 54 | "thumbrest" 55 | ], 56 | "axes":[ 57 | null, 58 | null, 59 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 60 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 61 | ] 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/registry/profiles/oculus/oculus-touch-v3.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "oculus-touch-v3", 3 | "fallbackProfileIds": [ "oculus-touch-v2", "oculus-touch", "generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "thumbrest" : { "type": "button" }, 14 | "menu" : { "type": "button" } 15 | }, 16 | "gamepad": { 17 | "mapping": "xr-standard", 18 | "buttons": [ 19 | "xr-standard-trigger", 20 | "xr-standard-squeeze", 21 | null, 22 | "xr-standard-thumbstick", 23 | "x-button", 24 | "y-button", 25 | "thumbrest", 26 | "menu" 27 | ], 28 | "axes":[ 29 | null, 30 | null, 31 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 32 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 33 | ] 34 | } 35 | }, 36 | "right": { 37 | "selectComponentId": "xr-standard-trigger", 38 | "components": { 39 | "xr-standard-trigger": { "type": "trigger" }, 40 | "xr-standard-squeeze": { "type": "squeeze" }, 41 | "xr-standard-thumbstick": { "type": "thumbstick" }, 42 | "a-button" : { "type": "button" }, 43 | "b-button" : { "type": "button" }, 44 | "thumbrest" : { "type": "button" } 45 | }, 46 | "gamepad": { 47 | "mapping": "xr-standard", 48 | "buttons": [ 49 | "xr-standard-trigger", 50 | "xr-standard-squeeze", 51 | null, 52 | "xr-standard-thumbstick", 53 | "a-button", 54 | "b-button", 55 | "thumbrest" 56 | ], 57 | "axes":[ 58 | null, 59 | null, 60 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 61 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 62 | ] 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/registry/profiles/oculus/oculus-touch.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "oculus-touch", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "thumbrest" : { "type": "button" }, 14 | "menu" : { "type": "button", "reserved": true } 15 | }, 16 | "gamepad": { 17 | "mapping": "xr-standard", 18 | "buttons": [ 19 | "xr-standard-trigger", 20 | "xr-standard-squeeze", 21 | null, 22 | "xr-standard-thumbstick", 23 | "x-button", 24 | "y-button", 25 | "thumbrest" 26 | ], 27 | "axes":[ 28 | null, 29 | null, 30 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 31 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 32 | ] 33 | } 34 | }, 35 | "right": { 36 | "selectComponentId": "xr-standard-trigger", 37 | "components": { 38 | "xr-standard-trigger": { "type": "trigger" }, 39 | "xr-standard-squeeze": { "type": "squeeze" }, 40 | "xr-standard-thumbstick": { "type": "thumbstick" }, 41 | "a-button" : { "type": "button" }, 42 | "b-button" : { "type": "button" }, 43 | "thumbrest" : { "type": "button" } 44 | }, 45 | "gamepad": { 46 | "mapping": "xr-standard", 47 | "buttons": [ 48 | "xr-standard-trigger", 49 | "xr-standard-squeeze", 50 | null, 51 | "xr-standard-thumbstick", 52 | "a-button", 53 | "b-button", 54 | "thumbrest" 55 | ], 56 | "axes":[ 57 | null, 58 | null, 59 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 60 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 61 | ] 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/registry/profiles/pico/pico-4.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "pico-4", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" }, 41 | "menu" : { "type": "button", "reserved": true } 42 | }, 43 | "gamepad": { 44 | "mapping": "xr-standard", 45 | "buttons": [ 46 | "xr-standard-trigger", 47 | "xr-standard-squeeze", 48 | null, 49 | "xr-standard-thumbstick", 50 | "a-button", 51 | "b-button" 52 | ], 53 | "axes":[ 54 | null, 55 | null, 56 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 57 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 58 | ] 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/registry/profiles/pico/pico-g2.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "pico-g2", 3 | "fallbackProfileIds": [ "generic-trigger-touchpad"], 4 | "layouts": { 5 | "left-right-none": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" } 10 | }, 11 | "gamepad": { 12 | "mapping": "xr-standard", 13 | "buttons": [ 14 | "xr-standard-trigger", 15 | null, 16 | "xr-standard-touchpad" 17 | ], 18 | "axes":[ 19 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 20 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 21 | ] 22 | 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /packages/registry/profiles/pico/pico-neo2.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "pico-neo2", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" }, 41 | "menu" : { "type": "button", "reserved": true } 42 | }, 43 | "gamepad": { 44 | "mapping": "xr-standard", 45 | "buttons": [ 46 | "xr-standard-trigger", 47 | "xr-standard-squeeze", 48 | null, 49 | "xr-standard-thumbstick", 50 | "a-button", 51 | "b-button" 52 | ], 53 | "axes":[ 54 | null, 55 | null, 56 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 57 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 58 | ] 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/registry/profiles/pico/pico-neo3.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "pico-neo3", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" }, 41 | "menu" : { "type": "button", "reserved": true } 42 | }, 43 | "gamepad": { 44 | "mapping": "xr-standard", 45 | "buttons": [ 46 | "xr-standard-trigger", 47 | "xr-standard-squeeze", 48 | null, 49 | "xr-standard-thumbstick", 50 | "a-button", 51 | "b-button" 52 | ], 53 | "axes":[ 54 | null, 55 | null, 56 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 57 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 58 | ] 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/registry/profiles/samsung/samsung-gearvr.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "samsung-gearvr", 3 | "fallbackProfileIds": [ "generic-trigger-touchpad"], 4 | "layouts" : { 5 | "left-right-none" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-touchpad": { "type": "touchpad" }, 10 | "menu" : { "type": "button" } 11 | }, 12 | "gamepad": { 13 | "mapping": "xr-standard", 14 | "buttons": [ 15 | "xr-standard-trigger", 16 | null, 17 | "xr-standard-touchpad", 18 | null, 19 | "menu" 20 | ], 21 | "axes":[ 22 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 23 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"} 24 | ] 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /packages/registry/profiles/samsung/samsung-odyssey.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "samsung-odyssey", 3 | "fallbackProfileIds": [ "microsoft-mixed-reality", "generic-trigger-squeeze-touchpad-thumbstick"], 4 | "layouts" : { 5 | "left-right" : { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" }, 11 | "xr-standard-thumbstick": { "type": "thumbstick" }, 12 | "menu" : { "type": "button", "reserved": true } 13 | }, 14 | "gamepad" : { 15 | "mapping": "xr-standard", 16 | "buttons": [ 17 | "xr-standard-trigger", 18 | "xr-standard-squeeze", 19 | "xr-standard-touchpad", 20 | "xr-standard-thumbstick" 21 | ], 22 | "axes":[ 23 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 24 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"}, 25 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 26 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 27 | ] 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/registry/profiles/valve/valve-index.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "valve-index", 3 | "fallbackProfileIds": [ "generic-trigger-squeeze-touchpad-thumbstick"], 4 | "layouts": { 5 | "left-right": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-touchpad": { "type": "touchpad" }, 11 | "xr-standard-thumbstick": { "type": "thumbstick" }, 12 | "a-button" : { "type": "button" }, 13 | "b-button" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | "xr-standard-touchpad", 21 | "xr-standard-thumbstick", 22 | "a-button" 23 | ], 24 | "axes":[ 25 | { "componentId": "xr-standard-touchpad", "axis": "x-axis"}, 26 | { "componentId": "xr-standard-touchpad", "axis": "y-axis"}, 27 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 28 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 29 | ] 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/registry/profiles/yvr/yvr-touch-v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "yvr-touch-v2", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" } 41 | }, 42 | "gamepad": { 43 | "mapping": "xr-standard", 44 | "buttons": [ 45 | "xr-standard-trigger", 46 | "xr-standard-squeeze", 47 | null, 48 | "xr-standard-thumbstick", 49 | "a-button", 50 | "b-button" 51 | ], 52 | "axes":[ 53 | null, 54 | null, 55 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 56 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 57 | ] 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/registry/profiles/yvr/yvr-touch.json: -------------------------------------------------------------------------------- 1 | { 2 | "profileId": "yvr-touch", 3 | "fallbackProfileIds": ["generic-trigger-squeeze-thumbstick"], 4 | "layouts": { 5 | "left": { 6 | "selectComponentId": "xr-standard-trigger", 7 | "components": { 8 | "xr-standard-trigger": { "type": "trigger" }, 9 | "xr-standard-squeeze": { "type": "squeeze" }, 10 | "xr-standard-thumbstick": { "type": "thumbstick" }, 11 | "x-button" : { "type": "button" }, 12 | "y-button" : { "type": "button" }, 13 | "menu" : { "type": "button", "reserved": true } 14 | }, 15 | "gamepad": { 16 | "mapping": "xr-standard", 17 | "buttons": [ 18 | "xr-standard-trigger", 19 | "xr-standard-squeeze", 20 | null, 21 | "xr-standard-thumbstick", 22 | "x-button", 23 | "y-button" 24 | ], 25 | "axes":[ 26 | null, 27 | null, 28 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 29 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 30 | ] 31 | } 32 | }, 33 | "right": { 34 | "selectComponentId": "xr-standard-trigger", 35 | "components": { 36 | "xr-standard-trigger": { "type": "trigger" }, 37 | "xr-standard-squeeze": { "type": "squeeze" }, 38 | "xr-standard-thumbstick": { "type": "thumbstick" }, 39 | "a-button" : { "type": "button" }, 40 | "b-button" : { "type": "button" } 41 | }, 42 | "gamepad": { 43 | "mapping": "xr-standard", 44 | "buttons": [ 45 | "xr-standard-trigger", 46 | "xr-standard-squeeze", 47 | null, 48 | "xr-standard-thumbstick", 49 | "a-button", 50 | "b-button" 51 | ], 52 | "axes":[ 53 | null, 54 | null, 55 | { "componentId": "xr-standard-thumbstick", "axis": "x-axis"}, 56 | { "componentId": "xr-standard-thumbstick", "axis": "y-axis"} 57 | ] 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/registry/schemas/common.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/registry/common.schema.json", 4 | "definitions": { 5 | "profileId": { 6 | "description": "A properly formatted string uniquely identifying a profile", 7 | "type": "string", 8 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)+$" 9 | }, 10 | "componentId": { 11 | "description": "A well-formatted component id", 12 | "type": "string", 13 | "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /packages/registry/schemas/gamepad.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/registry/gamepad.schema.json", 4 | "type": "object", 5 | "description": "The alignment of components to gamepad indices", 6 | "additionalProperties": false, 7 | "required": [ "mapping", "buttons", "axes" ], 8 | "properties": { 9 | "mapping": { 10 | "description": "The Gamepad.mapping value", 11 | "type": "string", 12 | "enum": [ "", "xr-standard"] 13 | }, 14 | "buttons": { 15 | "description": "An array that maps to the entries in the Gamepad's buttons array", 16 | "type": "array", 17 | "items": { 18 | "oneOf": [ 19 | { "type": "null" }, 20 | { "$ref": "common.schema.json#/definitions/componentId" } 21 | ] 22 | } 23 | }, 24 | "axes": { 25 | "description": "An array that maps to the entries in the Gamepad's axes array", 26 | "type": "array", 27 | "items": { 28 | "oneOf": [ 29 | { 30 | "type": "null" 31 | }, 32 | { 33 | "description": "A description of which component and which axis on that component is represented by this entry in the Gamepad's axes array", 34 | "type": "object", 35 | "required": [ "componentId", "axis" ], 36 | "properties": { 37 | "componentId": { "$ref": "common.schema.json#/definitions/componentId" }, 38 | "axis": { 39 | "type": "string", 40 | "enum": [ "x-axis", "y-axis"] 41 | } 42 | } 43 | } 44 | ] 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /packages/registry/schemas/layout.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/registry/layout.schema.json", 4 | "type": "object", 5 | "description": "A layout", 6 | "additionalProperties": false, 7 | "required": [ "components", "selectComponentId" ], 8 | "properties": { 9 | "selectComponentId": { "$ref": "common.schema.json#/definitions/componentId" }, 10 | "components": { 11 | "description": "The collection of components in the layout", 12 | "type": "object", 13 | "minProperties": 1, 14 | "additionalProperties": false, 15 | "patternProperties": { 16 | "^[a-z0-9]+(-[a-z0-9]+)*$": { 17 | "type": "object", 18 | "required": [ "type" ], 19 | "additionalProperties": false, 20 | "properties": { 21 | "type": { 22 | "type": "string", 23 | "enum": [ "trigger", "squeeze", "touchpad", "thumbstick", "button"] 24 | }, 25 | "reserved": { 26 | "type": "boolean" 27 | } 28 | } 29 | } 30 | } 31 | }, 32 | "gamepad": { "#ref": "gamepad.schema.json" } 33 | } 34 | } -------------------------------------------------------------------------------- /packages/registry/schemas/profile.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "https://immersive-web/webxr-input-profiles/registry/profile.schema.json", 4 | "type": "object", 5 | "description": "The root object for an XRInputSource profile.", 6 | "additionalProperties": true, 7 | "required": [ "profileId", "fallbackProfileIds", "layouts"], 8 | "properties": { 9 | "profileId" : { "$ref": "common.schema.json#/definitions/profileId" }, 10 | "fallbackProfileIds" : { 11 | "type": "array", 12 | "items": { "$ref": "common.schema.json#/definitions/profileId" }, 13 | "uniqueItems": true 14 | }, 15 | "deprecatedProfileIds" : { 16 | "type": "array", 17 | "items": { "$ref": "common.schema.json#/definitions/profileId" }, 18 | "uniqueItems": true 19 | }, 20 | "layouts" : { 21 | "type": "object", 22 | "additionalProperties": false, 23 | "minProperties": 1, 24 | "properties": { 25 | "none": { "$ref": "layout.schema.json" }, 26 | "left": { "$ref": "layout.schema.json" }, 27 | "right": { "$ref": "layout.schema.json" }, 28 | "left-right": { "$ref": "layout.schema.json" }, 29 | "left-right-none": { "$ref": "layout.schema.json" } 30 | }, 31 | "dependencies": { 32 | "left-right-none": { "not": { "required": ["none", "left", "right", "left-right"] } }, 33 | "left-right": { "not": { "required": ["left", "right", "left-right-none"] } }, 34 | "left": { "required": ["right"], "not": { "required": ["left-right", "left-right-none"] } }, 35 | "right": { "required": ["left"], "not": { "required": ["left-right", "left-right-none"] } }, 36 | "none": { "not": { "required": ["left-right-none"] } } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /packages/viewer/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Amazon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice (including the next 13 | paragraph) shall be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 21 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/viewer/README.md: -------------------------------------------------------------------------------- 1 | # WebXR Input Profiles - Viewer 2 | 3 | [![Build Status](https://travis-ci.com/immersive-web/webxr-input-profiles.svg?branch=master)](https://travis-ci.org/immersive-web/webxr-input-profiles) 4 | 5 | ## Overview 6 | This package builds a test page that allows for easy testing and viewing of the devices described by each profile in this repository. It renders the assets described by the profile and allows emulated manipulation of each of the inputs in order to see the rendered response. 7 | 8 | The latest state of the master branch can be tried out in the hosted [Profile Viewer](https://immersive-web.github.io/webxr-input-profiles/packages/viewer/dist/index.html) 9 | 10 | ## Contributing 11 | 12 | ### Filing a bug 13 | To file bugs, use this [issue template](https://github.com/immersive-web/webxr-input-profiles/issues/new?assignees=&labels=viewer&template=viewer-bug-report.md&title=) 14 | 15 | ### Development 16 | In general, this package should be built and tested from the root of the repository using the following command: 17 | > npm run test 18 | 19 | To build just this package without running tests, invoke the following command from the root of the repository: 20 | > npm run build -- --scope @webxr-input-profiles/viewer 21 | 22 | ### Licence 23 | See the [LICENSE.md](LICENSE.md). 24 | 25 | ## Usage 26 | 27 | ### Getting started 28 | To install this library: 29 | ``` 30 | npm install @webxr-input-profiles/viewer 31 | ``` 32 | -------------------------------------------------------------------------------- /packages/viewer/backgrounds/README.md: -------------------------------------------------------------------------------- 1 | # Backgrounds 2 | 3 | These various backgrounds are provided to show what the controllers look like in a variety of lighting conditions, which can help when debugging material issues. 4 | 5 | All background images courtesy of [HDRI Haven](https://hdrihaven.com/) 6 | -------------------------------------------------------------------------------- /packages/viewer/backgrounds/autumn_forest_01_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/autumn_forest_01_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/backgrounds/backgrounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "autumn_forest": "autumn_forest_01_2k.hdr", 3 | "cave_wall": "cave_wall_2k.hdr", 4 | "fireplace": "fireplace_2k.hdr", 5 | "georgentor": "georgentor_2k.hdr", 6 | "snowy_park": "snowy_park_01_2k.hdr", 7 | "studio": "studio_small_03_2k.hdr" 8 | } -------------------------------------------------------------------------------- /packages/viewer/backgrounds/cave_wall_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/cave_wall_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/backgrounds/fireplace_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/fireplace_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/backgrounds/georgentor_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/georgentor_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/backgrounds/snowy_park_01_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/snowy_park_01_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/backgrounds/studio_small_03_2k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-input-profiles/483489b7aee2913e019cd70bc978c537e406f19e/packages/viewer/backgrounds/studio_small_03_2k.hdr -------------------------------------------------------------------------------- /packages/viewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webxr-input-profiles/viewer", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "A viewer", 6 | "main": "dist/index.js", 7 | "files": [ 8 | "package.json", 9 | "LICENSE", 10 | "README.md", 11 | "dist/**" 12 | ], 13 | "scripts": { 14 | "clean": "node -e \"try { require('fs').rmdirSync('./dist', { recursive: true }); } catch {}\"", 15 | "build": "rollup -c -m inline", 16 | "cleanBuild": "npm run clean && npm run build", 17 | "watch": "rollup -c -w -m inline", 18 | "test": "echo \"Run tests from root\" && exit 1" 19 | }, 20 | "dependencies": { 21 | "@webxr-input-profiles/assets": "^1.0.0", 22 | "@webxr-input-profiles/motion-controllers": "^1.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/viewer/rollup.config.js: -------------------------------------------------------------------------------- 1 | import copy from 'rollup-plugin-copy-glob'; 2 | 3 | const DIST_FOLDER = 'dist'; 4 | 5 | export default [ 6 | { 7 | input: ['src/modelViewer.js'], 8 | output: [ 9 | { 10 | format: 'es', 11 | file: `${DIST_FOLDER}/modelViewer.js` 12 | } 13 | ], 14 | external: [ 15 | './three/build/three.module.js', 16 | './three/examples/jsm/loaders/RGBELoader.js', 17 | './three/examples/jsm/loaders/GLTFLoader.js', 18 | './three/examples/jsm/loaders/FBXLoader.js', 19 | './three/examples/jsm/controls/OrbitControls.js', 20 | './three/examples/jsm/webxr/VRButton.js', 21 | './ajv/ajv.min.js', 22 | './motion-controllers.js', 23 | '../motion-controllers.js', 24 | './registryTools/validateRegistryProfile.js', 25 | './assetTools/expandRegistryProfile.js', 26 | './assetTools/buildAssetProfile.js' 27 | ], 28 | plugins: [ 29 | copy( 30 | [ 31 | { files: 'src/*.{html,css}', dest: `${DIST_FOLDER}` } 32 | ], 33 | { verbose: true, watch: process.env.ROLLUP_WATCH } 34 | ), 35 | copy( 36 | [ 37 | { files: 'backgrounds/*.{hdr,json}', dest: `${DIST_FOLDER}/backgrounds` } 38 | ], 39 | { verbose: true, watch: process.env.ROLLUP_WATCH } 40 | ), 41 | copy( 42 | [ 43 | { files: '../../node_modules/three/**', dest: `${DIST_FOLDER}/three` } 44 | ], 45 | { verbose: false, watch: process.env.ROLLUP_WATCH } 46 | ), 47 | copy( 48 | [ 49 | { files: '../assets/dist/profiles/**', dest: `${DIST_FOLDER}/profiles` } 50 | ], 51 | { verbose: true, watch: process.env.ROLLUP_WATCH } 52 | ), 53 | copy( 54 | [ 55 | { files: '../registry/dist/profilesTools/**', dest: `${DIST_FOLDER}/registryTools` } 56 | ], 57 | { verbose: true, watch: process.env.ROLLUP_WATCH } 58 | ), 59 | copy( 60 | [ 61 | { files: '../assets/dist/profilesTools/**', dest: `${DIST_FOLDER}/assetTools` } 62 | ], 63 | { verbose: true, watch: process.env.ROLLUP_WATCH } 64 | ), 65 | copy( 66 | [ 67 | { files: '../../node_modules/ajv/dist/ajv.min.js*', dest: `${DIST_FOLDER}/ajv` } 68 | ], 69 | { verbose: true, watch: process.env.ROLLUP_WATCH } 70 | ), 71 | copy( 72 | [ 73 | { files: '../motion-controllers/dist/*.js', dest: `${DIST_FOLDER}` } 74 | ], 75 | { verbose: true, watch: process.env.ROLLUP_WATCH } 76 | ) 77 | ] 78 | } 79 | ]; 80 | -------------------------------------------------------------------------------- /packages/viewer/src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "import/extensions": ["error", "always"] 4 | } 5 | } -------------------------------------------------------------------------------- /packages/viewer/src/assetError.js: -------------------------------------------------------------------------------- 1 | let errorsSectionElement; 2 | let errorsListElement; 3 | class AssetError extends Error { 4 | constructor(...params) { 5 | super(...params); 6 | AssetError.log(this.message); 7 | } 8 | 9 | static initialize() { 10 | errorsListElement = document.getElementById('errors'); 11 | errorsSectionElement = document.getElementById('errors'); 12 | } 13 | 14 | static log(errorMessage) { 15 | const itemElement = document.createElement('li'); 16 | itemElement.innerText = errorMessage; 17 | errorsListElement.appendChild(itemElement); 18 | errorsSectionElement.hidden = false; 19 | } 20 | 21 | static clearAll() { 22 | errorsListElement.innerHTML = ''; 23 | errorsSectionElement.hidden = true; 24 | } 25 | } 26 | 27 | export default AssetError; 28 | -------------------------------------------------------------------------------- /packages/viewer/src/backgroundSelector.js: -------------------------------------------------------------------------------- 1 | const defaultBackground = 'georgentor'; 2 | 3 | export default class BackgroundSelector extends EventTarget { 4 | constructor() { 5 | super(); 6 | 7 | this.backgroundSelectorElement = document.getElementById('backgroundSelector'); 8 | this.backgroundSelectorElement.addEventListener('change', () => { this.onBackgroundChange(); }); 9 | 10 | this.selectedBackground = window.localStorage.getItem('background') || defaultBackground; 11 | this.backgroundList = {}; 12 | fetch('backgrounds/backgrounds.json') 13 | .then(response => response.json()) 14 | .then((backgrounds) => { 15 | this.backgroundList = backgrounds; 16 | Object.keys(backgrounds).forEach((background) => { 17 | const option = document.createElement('option'); 18 | option.value = background; 19 | option.innerText = background; 20 | if (this.selectedBackground === background) { 21 | option.selected = true; 22 | } 23 | this.backgroundSelectorElement.appendChild(option); 24 | }); 25 | this.dispatchEvent(new Event('selectionchange')); 26 | }); 27 | } 28 | 29 | onBackgroundChange() { 30 | this.selectedBackground = this.backgroundSelectorElement.value; 31 | window.localStorage.setItem('background', this.selectedBackground); 32 | this.dispatchEvent(new Event('selectionchange')); 33 | } 34 | 35 | get backgroundPath() { 36 | return this.backgroundList[this.selectedBackground]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/viewer/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebXR Input Profile Viewer 6 | 7 | 8 | 9 | 10 |
11 |
12 |

WebXR Input Profile Viewer

13 |
14 |
15 | instructions 16 |

The dropdown on the left side of this page allows for manually testing all profiles with 3D assets available in the webxr-input-profiles repository. A profile from the local file system can also be tested by selecting the relevant files in "Local Profile" file selector. These files must include a hardware description JSON file conforming to the requirements in the @webxr-input-profiles/registry package, at least one matching 3D asset which conforms to the requirements in the @webxr-input-profiles/assets package, and, optionally, an asset description override JSON file that also conforms to the requirements in the @webxr-input-profiles/assets package. When entering VR, the best matched 3D assets for the currently connected XR hardware will be shown and animated. The locally loaded profile will be used if it is the best match. 17 |

Source code for this sample page, which happens to be written in three.js, can be found in the @webxr-input-profiles/viewer package. However, very little of the code is specific to three.js. Other engines can adopt the same functionality by integrating with the @webxr-input-profiles/motion-controllers package. 18 |

19 |
20 |
21 | 22 | 23 | 24 |
25 | 26 | 27 |
28 | 29 | 30 |
31 | 32 | 33 |
34 |
    35 |
    36 | 39 | 43 |
    44 |
      45 |
    46 |
    47 |
    48 |
    49 |
    50 | 51 | 52 |
    53 | 54 |
    55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /packages/viewer/src/manualControls.js: -------------------------------------------------------------------------------- 1 | let motionController; 2 | let mockGamepad; 3 | let controlsListElement; 4 | 5 | function updateText() { 6 | if (motionController) { 7 | Object.values(motionController.components).forEach((component) => { 8 | const dataElement = document.getElementById(`${component.id}_data`); 9 | dataElement.innerHTML = JSON.stringify(component.data, null, 2); 10 | }); 11 | } 12 | } 13 | 14 | function onButtonValueChange(event) { 15 | const { index } = event.target.dataset; 16 | mockGamepad.buttons[index].value = Number(event.target.value); 17 | } 18 | 19 | function onAxisValueChange(event) { 20 | const { index } = event.target.dataset; 21 | mockGamepad.axes[index] = Number(event.target.value); 22 | } 23 | 24 | function clear() { 25 | motionController = undefined; 26 | mockGamepad = undefined; 27 | 28 | if (!controlsListElement) { 29 | controlsListElement = document.getElementById('controlsList'); 30 | } 31 | controlsListElement.innerHTML = ''; 32 | } 33 | 34 | function addButtonControls(componentControlsElement, buttonIndex) { 35 | const buttonControlsElement = document.createElement('div'); 36 | buttonControlsElement.setAttribute('class', 'componentControls'); 37 | 38 | buttonControlsElement.innerHTML += ` 39 | 40 | 41 | `; 42 | 43 | componentControlsElement.appendChild(buttonControlsElement); 44 | 45 | document.getElementById(`buttons[${buttonIndex}].value`).addEventListener('input', onButtonValueChange); 46 | } 47 | 48 | function addAxisControls(componentControlsElement, axisName, axisIndex) { 49 | const axisControlsElement = document.createElement('div'); 50 | axisControlsElement.setAttribute('class', 'componentControls'); 51 | 52 | axisControlsElement.innerHTML += ` 53 |