Skip to content

Commit

Permalink
First commit. Initial XRControllers implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
sniok committed May 11, 2020
0 parents commit 55ce09f
Show file tree
Hide file tree
Showing 10 changed files with 7,398 additions and 0 deletions.
83 changes: 83 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"parser": "babel-eslint",
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings",
"prettier"
],
"plugins": ["react-hooks", "react"],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"rules": {
"curly": ["warn", "multi-line", "consistent"],
"no-console": "off",
"no-dupe-class-members": "error",
"no-empty": "warn",
"no-undef": "error",
"no-unused-expressions": "error",
"no-unused-vars": "error",
"no-var": "warn",
"prefer-const": "warn",
"no-shadow": "warn",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/jsx-uses-vars": "error",
"react/jsx-uses-react": "error"
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"parserOptions": {
"ecmaFeatures": { "jsx": true },
"ecmaVersion": 2018,
"project": "./tsconfig.json",
"sourceType": "module"
},
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"prettier/@typescript-eslint"
],
"rules": {
"prefer-const": "off",
"import/no-unresolved": "off",
"import/named": "off",
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-inferrable-types": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/ban-ts-ignore": "off"
}
}
],
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".json", ".ts", ".tsx"],
"paths": ["src"]
}
}
}
}
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
node_modules/
coverage/
dist/
build/
types/
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
.DS_Store
.vscode
.docz/
package-lock.json
coverage/
.idea
yarn-error.log
.size-snapshot.json
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# react-xr

React components and hooks for creating VR/AR/XR applications with [react-three-fiber](https://github.com/react-spring/react-three-fiber)

**Note: Extremely early in development. Contributors welcome!**
67 changes: 67 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "react-xr",
"version": "0.0.1",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"license": "MIT",
"scripts": {
"prebuild": "rimraf dist && npm run typegen",
"build": "rollup -c",
"prepare": "npm run build",
"eslint": "eslint src/**/*.{js,ts,jsx,tsx}",
"test": "echo no tests yet",
"typecheck": "tsc --noEmit --emitDeclarationOnly false --strict --jsx react",
"typegen": "tsc && mv dist/src/* dist && rm -rf dist/src || true"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged --pattern '**/*.*(js|jsx|ts|tsx)'"
}
},
"devDependencies": {
"@babel/core": "7.9.0",
"@babel/plugin-transform-modules-commonjs": "7.9.0",
"@babel/plugin-transform-parameters": "7.9.5",
"@babel/plugin-transform-runtime": "7.9.0",
"@babel/plugin-transform-template-literals": "7.8.3",
"@babel/preset-env": "7.9.5",
"@babel/preset-react": "7.9.4",
"@babel/preset-typescript": "^7.9.0",
"@types/jest": "^25.2.1",
"@types/lodash-es": "^4.17.3",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"babel-eslint": "^10.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^3.0.0",
"husky": "^4.2.5",
"jest": "^25.4.0",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-three-fiber": "^4.1.1",
"rimraf": "^3.0.2",
"rollup": "^2.7.2",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-size-snapshot": "^0.11.0",
"three": "^0.115.0",
"typescript": "^3.8.3"
},
"peerDependencies": {
"react": ">=16.13",
"react-dom": ">=16.13",
"react-three-fiber": ">=4.0"
}
}
55 changes: 55 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import path from "path";
import babel from "rollup-plugin-babel";
import resolve from "rollup-plugin-node-resolve";
import json from "rollup-plugin-json";
import { sizeSnapshot } from "rollup-plugin-size-snapshot";

const root = process.platform === "win32" ? path.resolve("/") : "/";
const external = (id) => !id.startsWith(".") && !id.startsWith(root);
const extensions = [".js", ".jsx", ".ts", ".tsx", ".json"];

const getBabelOptions = ({ useESModules }, targets) => ({
babelrc: false,
extensions,
exclude: "**/node_modules/**",
runtimeHelpers: true,
presets: [
["@babel/preset-env", { loose: true, modules: false, targets }],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
["transform-react-remove-prop-types", { removeImport: true }],
["@babel/transform-runtime", { regenerator: false, useESModules }],
],
});

export default [
{
input: `./src/index.tsx`,
output: { file: `dist/index.js`, format: "esm" },
external,
plugins: [
json(),
babel(
getBabelOptions(
{ useESModules: true },
">1%, not dead, not ie 11, not op_mini all"
)
),
sizeSnapshot(),
resolve({ extensions }),
],
},
{
input: `./src/index.tsx`,
output: { file: `dist/index.cjs.js`, format: "cjs" },
external,
plugins: [
json(),
babel(getBabelOptions({ useESModules: false })),
sizeSnapshot(),
resolve({ extensions }),
],
},
];
74 changes: 74 additions & 0 deletions src/XRControllers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from "react";
import { WebGLRenderer, Group } from "three";
import { XRControllerModelFactory } from "three/examples/jsm/webxr/XRControllerModelFactory";
import { useThree } from "react-three-fiber";
import { XRInputSource } from "./webxr";

interface XRController {
inputSource?: XRInputSource;
/**
* Group with orientation that should be used to render virtual
* objects such that they appear to be held in the user’s hand
*/
grip: Group;
/** Group with orientation of the preferred pointing ray */
controller: Group;
}
const XRController = {
make: (id: number, gl: WebGLRenderer): XRController => {
const controller = gl.xr.getController(id);
const grip = gl.xr.getControllerGrip(id);

const xrController = {
inputSource: undefined,
grip,
controller,
};

controller.addEventListener("connected", (e) => {
xrController.inputSource = e.data;
});

return xrController;
},
};

const XRControllersContext = React.createContext<XRController[]>([]);
export function XRControllers(props: { children: React.ReactNode }) {
const { gl } = useThree();
const [controllers, setControllers] = React.useState<XRController[]>([]);

React.useEffect(() => {
setControllers([0, 1].map((id) => XRController.make(id, gl)));
}, [gl]);

return (
<XRControllersContext.Provider value={controllers}>
{props.children}
</XRControllersContext.Provider>
);
}

export const useXRControllers = () => React.useContext(XRControllersContext);

export function DefaultXRControllerModels() {
const controllers = useXRControllers();

const modelFactory = React.useMemo(() => new XRControllerModelFactory(), []);

const models = React.useMemo(
() =>
controllers.map((it) => {
const model = modelFactory.createControllerModel(it.controller);

return (
<primitive object={it.grip} key={it.grip.id}>
<primitive object={model} />
</primitive>
);
}),
[controllers, modelFactory]
);

return <>{models}</>;
}
2 changes: 2 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./XRControllers";
export * from "./webxr";
18 changes: 18 additions & 0 deletions src/webxr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Types that should eventually land in lib.dom.d.ts
// Taken from WebXR spec
// https://www.w3.org/TR/webxr/

export type XRHandedness = "none" | "left" | "right";

export type XRTargetRayMode = "gaze" | "tracked-pointer" | "screen";

export type XRSpace = EventTarget;

export interface XRInputSource {
readonly handedness: XRHandedness;
readonly targetRayMode: XRTargetRayMode;
readonly gamepad?: Gamepad;
readonly targetRaySpace: XRSpace;
readonly gripSpace?: XRSpace;
readonly profiles: string;
}
21 changes: 21 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"jsx": "react",
"pretty": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"removeComments": true,
"emitDeclarationOnly": true,
"outDir": "dist",
"resolveJsonModule": true,
"noImplicitThis": false,
"baseUrl": "./src"
},
"include": ["./src"],
"exclude": ["./node_modules/**/*"]
}
Loading

0 comments on commit 55ce09f

Please sign in to comment.