Initial project import
This commit is contained in:
326
node_modules/three-stdlib/libs/MotionControllers.js
generated
vendored
Normal file
326
node_modules/three-stdlib/libs/MotionControllers.js
generated
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => {
|
||||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
return value;
|
||||
};
|
||||
const MotionControllerConstants = {
|
||||
Handedness: {
|
||||
NONE: "none",
|
||||
LEFT: "left",
|
||||
RIGHT: "right"
|
||||
},
|
||||
ComponentState: {
|
||||
DEFAULT: "default",
|
||||
TOUCHED: "touched",
|
||||
PRESSED: "pressed"
|
||||
},
|
||||
ComponentProperty: {
|
||||
BUTTON: "button",
|
||||
X_AXIS: "xAxis",
|
||||
Y_AXIS: "yAxis",
|
||||
STATE: "state"
|
||||
},
|
||||
ComponentType: {
|
||||
TRIGGER: "trigger",
|
||||
SQUEEZE: "squeeze",
|
||||
TOUCHPAD: "touchpad",
|
||||
THUMBSTICK: "thumbstick",
|
||||
BUTTON: "button"
|
||||
},
|
||||
ButtonTouchThreshold: 0.05,
|
||||
AxisTouchThreshold: 0.1,
|
||||
VisualResponseProperty: {
|
||||
TRANSFORM: "transform",
|
||||
VISIBILITY: "visibility"
|
||||
}
|
||||
};
|
||||
async function fetchJsonFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
async function fetchProfilesList(basePath) {
|
||||
if (!basePath) {
|
||||
throw new Error("No basePath supplied");
|
||||
}
|
||||
const profileListFileName = "profilesList.json";
|
||||
const profilesList = await fetchJsonFile(`${basePath}/${profileListFileName}`);
|
||||
return profilesList;
|
||||
}
|
||||
async function fetchProfile(xrInputSource, basePath, defaultProfile = null, getAssetPath = true) {
|
||||
if (!xrInputSource) {
|
||||
throw new Error("No xrInputSource supplied");
|
||||
}
|
||||
if (!basePath) {
|
||||
throw new Error("No basePath supplied");
|
||||
}
|
||||
const supportedProfilesList = await fetchProfilesList(basePath);
|
||||
let match = void 0;
|
||||
xrInputSource.profiles.some((profileId) => {
|
||||
const supportedProfile = supportedProfilesList[profileId];
|
||||
if (supportedProfile) {
|
||||
match = {
|
||||
profileId,
|
||||
profilePath: `${basePath}/${supportedProfile.path}`,
|
||||
deprecated: !!supportedProfile.deprecated
|
||||
};
|
||||
}
|
||||
return !!match;
|
||||
});
|
||||
if (!match) {
|
||||
if (!defaultProfile) {
|
||||
throw new Error("No matching profile name found");
|
||||
}
|
||||
const supportedProfile = supportedProfilesList[defaultProfile];
|
||||
if (!supportedProfile) {
|
||||
throw new Error(`No matching profile name found and default profile "${defaultProfile}" missing.`);
|
||||
}
|
||||
match = {
|
||||
profileId: defaultProfile,
|
||||
profilePath: `${basePath}/${supportedProfile.path}`,
|
||||
deprecated: !!supportedProfile.deprecated
|
||||
};
|
||||
}
|
||||
const profile = await fetchJsonFile(match.profilePath);
|
||||
let assetPath = void 0;
|
||||
if (getAssetPath) {
|
||||
let layout;
|
||||
if (xrInputSource.handedness === "any") {
|
||||
layout = profile.layouts[Object.keys(profile.layouts)[0]];
|
||||
} else {
|
||||
layout = profile.layouts[xrInputSource.handedness];
|
||||
}
|
||||
if (!layout) {
|
||||
throw new Error(`No matching handedness, ${xrInputSource.handedness}, in profile ${match.profileId}`);
|
||||
}
|
||||
if (layout.assetPath) {
|
||||
assetPath = match.profilePath.replace("profile.json", layout.assetPath);
|
||||
}
|
||||
}
|
||||
return { profile, assetPath };
|
||||
}
|
||||
const defaultComponentValues = {
|
||||
xAxis: 0,
|
||||
yAxis: 0,
|
||||
button: 0,
|
||||
state: MotionControllerConstants.ComponentState.DEFAULT
|
||||
};
|
||||
function normalizeAxes(x = 0, y = 0) {
|
||||
let xAxis = x;
|
||||
let yAxis = y;
|
||||
const hypotenuse = Math.sqrt(x * x + y * y);
|
||||
if (hypotenuse > 1) {
|
||||
const theta = Math.atan2(y, x);
|
||||
xAxis = Math.cos(theta);
|
||||
yAxis = Math.sin(theta);
|
||||
}
|
||||
const result = {
|
||||
normalizedXAxis: xAxis * 0.5 + 0.5,
|
||||
normalizedYAxis: yAxis * 0.5 + 0.5
|
||||
};
|
||||
return result;
|
||||
}
|
||||
class VisualResponse {
|
||||
constructor(visualResponseDescription) {
|
||||
__publicField(this, "value");
|
||||
__publicField(this, "componentProperty");
|
||||
__publicField(this, "states");
|
||||
__publicField(this, "valueNodeName");
|
||||
__publicField(this, "valueNodeProperty");
|
||||
__publicField(this, "minNodeName");
|
||||
__publicField(this, "maxNodeName");
|
||||
__publicField(this, "valueNode");
|
||||
__publicField(this, "minNode");
|
||||
__publicField(this, "maxNode");
|
||||
this.componentProperty = visualResponseDescription.componentProperty;
|
||||
this.states = visualResponseDescription.states;
|
||||
this.valueNodeName = visualResponseDescription.valueNodeName;
|
||||
this.valueNodeProperty = visualResponseDescription.valueNodeProperty;
|
||||
if (this.valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM) {
|
||||
this.minNodeName = visualResponseDescription.minNodeName;
|
||||
this.maxNodeName = visualResponseDescription.maxNodeName;
|
||||
}
|
||||
this.value = 0;
|
||||
this.updateFromComponent(defaultComponentValues);
|
||||
}
|
||||
/**
|
||||
* Computes the visual response's interpolation weight based on component state
|
||||
* @param {Object} componentValues - The component from which to update
|
||||
* @param {number | undefined} xAxis - The reported X axis value of the component
|
||||
* @param {number | undefined} yAxis - The reported Y axis value of the component
|
||||
* @param {number | undefined} button - The reported value of the component's button
|
||||
* @param {string} state - The component's active state
|
||||
*/
|
||||
updateFromComponent({
|
||||
xAxis,
|
||||
yAxis,
|
||||
button,
|
||||
state
|
||||
}) {
|
||||
const { normalizedXAxis, normalizedYAxis } = normalizeAxes(xAxis, yAxis);
|
||||
switch (this.componentProperty) {
|
||||
case MotionControllerConstants.ComponentProperty.X_AXIS:
|
||||
this.value = this.states.includes(state) ? normalizedXAxis : 0.5;
|
||||
break;
|
||||
case MotionControllerConstants.ComponentProperty.Y_AXIS:
|
||||
this.value = this.states.includes(state) ? normalizedYAxis : 0.5;
|
||||
break;
|
||||
case MotionControllerConstants.ComponentProperty.BUTTON:
|
||||
this.value = this.states.includes(state) && button ? button : 0;
|
||||
break;
|
||||
case MotionControllerConstants.ComponentProperty.STATE:
|
||||
if (this.valueNodeProperty === MotionControllerConstants.VisualResponseProperty.VISIBILITY) {
|
||||
this.value = this.states.includes(state);
|
||||
} else {
|
||||
this.value = this.states.includes(state) ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unexpected visualResponse componentProperty ${this.componentProperty}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Component {
|
||||
/**
|
||||
* @param {string} componentId - Id of the component
|
||||
* @param {InputProfileComponent} componentDescription - Description of the component to be created
|
||||
*/
|
||||
constructor(componentId, componentDescription) {
|
||||
__publicField(this, "id");
|
||||
__publicField(this, "values");
|
||||
__publicField(this, "type");
|
||||
__publicField(this, "gamepadIndices");
|
||||
__publicField(this, "rootNodeName");
|
||||
__publicField(this, "visualResponses");
|
||||
__publicField(this, "touchPointNodeName");
|
||||
__publicField(this, "touchPointNode");
|
||||
if (!componentId || !componentDescription || !componentDescription.visualResponses || !componentDescription.gamepadIndices || Object.keys(componentDescription.gamepadIndices).length === 0) {
|
||||
throw new Error("Invalid arguments supplied");
|
||||
}
|
||||
this.id = componentId;
|
||||
this.type = componentDescription.type;
|
||||
this.rootNodeName = componentDescription.rootNodeName;
|
||||
this.touchPointNodeName = componentDescription.touchPointNodeName;
|
||||
this.visualResponses = {};
|
||||
Object.keys(componentDescription.visualResponses).forEach((responseName) => {
|
||||
const visualResponse = new VisualResponse(componentDescription.visualResponses[responseName]);
|
||||
this.visualResponses[responseName] = visualResponse;
|
||||
});
|
||||
this.gamepadIndices = Object.assign({}, componentDescription.gamepadIndices);
|
||||
this.values = {
|
||||
state: MotionControllerConstants.ComponentState.DEFAULT,
|
||||
button: this.gamepadIndices.button !== void 0 ? 0 : void 0,
|
||||
xAxis: this.gamepadIndices.xAxis !== void 0 ? 0 : void 0,
|
||||
yAxis: this.gamepadIndices.yAxis !== void 0 ? 0 : void 0
|
||||
};
|
||||
}
|
||||
get data() {
|
||||
const data = { id: this.id, ...this.values };
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* @description Poll for updated data based on current gamepad state
|
||||
* @param {Object} gamepad - The gamepad object from which the component data should be polled
|
||||
*/
|
||||
updateFromGamepad(gamepad) {
|
||||
this.values.state = MotionControllerConstants.ComponentState.DEFAULT;
|
||||
if (this.gamepadIndices.button !== void 0 && gamepad.buttons.length > this.gamepadIndices.button) {
|
||||
const gamepadButton = gamepad.buttons[this.gamepadIndices.button];
|
||||
this.values.button = gamepadButton.value;
|
||||
this.values.button = this.values.button < 0 ? 0 : this.values.button;
|
||||
this.values.button = this.values.button > 1 ? 1 : this.values.button;
|
||||
if (gamepadButton.pressed || this.values.button === 1) {
|
||||
this.values.state = MotionControllerConstants.ComponentState.PRESSED;
|
||||
} else if (gamepadButton.touched || this.values.button > MotionControllerConstants.ButtonTouchThreshold) {
|
||||
this.values.state = MotionControllerConstants.ComponentState.TOUCHED;
|
||||
}
|
||||
}
|
||||
if (this.gamepadIndices.xAxis !== void 0 && gamepad.axes.length > this.gamepadIndices.xAxis) {
|
||||
this.values.xAxis = gamepad.axes[this.gamepadIndices.xAxis];
|
||||
this.values.xAxis = this.values.xAxis < -1 ? -1 : this.values.xAxis;
|
||||
this.values.xAxis = this.values.xAxis > 1 ? 1 : this.values.xAxis;
|
||||
if (this.values.state === MotionControllerConstants.ComponentState.DEFAULT && Math.abs(this.values.xAxis) > MotionControllerConstants.AxisTouchThreshold) {
|
||||
this.values.state = MotionControllerConstants.ComponentState.TOUCHED;
|
||||
}
|
||||
}
|
||||
if (this.gamepadIndices.yAxis !== void 0 && gamepad.axes.length > this.gamepadIndices.yAxis) {
|
||||
this.values.yAxis = gamepad.axes[this.gamepadIndices.yAxis];
|
||||
this.values.yAxis = this.values.yAxis < -1 ? -1 : this.values.yAxis;
|
||||
this.values.yAxis = this.values.yAxis > 1 ? 1 : this.values.yAxis;
|
||||
if (this.values.state === MotionControllerConstants.ComponentState.DEFAULT && Math.abs(this.values.yAxis) > MotionControllerConstants.AxisTouchThreshold) {
|
||||
this.values.state = MotionControllerConstants.ComponentState.TOUCHED;
|
||||
}
|
||||
}
|
||||
Object.values(this.visualResponses).forEach((visualResponse) => {
|
||||
visualResponse.updateFromComponent(this.values);
|
||||
});
|
||||
}
|
||||
}
|
||||
class MotionController {
|
||||
/**
|
||||
* @param {XRInputSource} xrInputSource - The XRInputSource to build the MotionController around
|
||||
* @param {Profile} profile - The best matched profile description for the supplied xrInputSource
|
||||
* @param {string} assetUrl
|
||||
*/
|
||||
constructor(xrInputSource, profile, assetUrl) {
|
||||
__publicField(this, "xrInputSource");
|
||||
__publicField(this, "assetUrl");
|
||||
__publicField(this, "layoutDescription");
|
||||
__publicField(this, "id");
|
||||
__publicField(this, "components");
|
||||
if (!xrInputSource) {
|
||||
throw new Error("No xrInputSource supplied");
|
||||
}
|
||||
if (!profile) {
|
||||
throw new Error("No profile supplied");
|
||||
}
|
||||
if (!profile.layouts[xrInputSource.handedness]) {
|
||||
throw new Error("No layout for " + xrInputSource.handedness + " handedness");
|
||||
}
|
||||
this.xrInputSource = xrInputSource;
|
||||
this.assetUrl = assetUrl;
|
||||
this.id = profile.profileId;
|
||||
this.layoutDescription = profile.layouts[xrInputSource.handedness];
|
||||
this.components = {};
|
||||
Object.keys(this.layoutDescription.components).forEach((componentId) => {
|
||||
const componentDescription = this.layoutDescription.components[componentId];
|
||||
this.components[componentId] = new Component(componentId, componentDescription);
|
||||
});
|
||||
this.updateFromGamepad();
|
||||
}
|
||||
get gripSpace() {
|
||||
return this.xrInputSource.gripSpace;
|
||||
}
|
||||
get targetRaySpace() {
|
||||
return this.xrInputSource.targetRaySpace;
|
||||
}
|
||||
/**
|
||||
* @description Returns a subset of component data for simplified debugging
|
||||
*/
|
||||
get data() {
|
||||
const data = [];
|
||||
Object.values(this.components).forEach((component) => {
|
||||
data.push(component.data);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* @description Poll for updated data based on current gamepad state
|
||||
*/
|
||||
updateFromGamepad() {
|
||||
Object.values(this.components).forEach((component) => {
|
||||
component.updateFromGamepad(this.xrInputSource.gamepad);
|
||||
});
|
||||
}
|
||||
}
|
||||
export {
|
||||
MotionController,
|
||||
MotionControllerConstants,
|
||||
fetchProfile,
|
||||
fetchProfilesList
|
||||
};
|
||||
//# sourceMappingURL=MotionControllers.js.map
|
||||
Reference in New Issue
Block a user