
com.onemillionworlds.tamarin.actions.actionprofile.Action Maven / Gradle / Ivy
Show all versions of tamarin Show documentation
package com.onemillionworlds.tamarin.actions.actionprofile;
import com.onemillionworlds.tamarin.actions.ActionType;
import com.onemillionworlds.tamarin.actions.HandSide;
import com.onemillionworlds.tamarin.actions.controllerprofile.GoogleDaydreamController;
import com.onemillionworlds.tamarin.actions.controllerprofile.HtcProVive;
import com.onemillionworlds.tamarin.actions.controllerprofile.HtcViveController;
import com.onemillionworlds.tamarin.actions.controllerprofile.KhronosSimpleController;
import com.onemillionworlds.tamarin.actions.controllerprofile.MixedRealityMotionController;
import com.onemillionworlds.tamarin.actions.controllerprofile.OculusGoController;
import com.onemillionworlds.tamarin.actions.controllerprofile.OculusTouchController;
import com.onemillionworlds.tamarin.actions.controllerprofile.ValveIndexController;
import java.util.ArrayList;
import java.util.List;
public class Action {
/**
* By default, all actions support the left and right hand sides.
*/
public static List DEFAULT_SUB_ACTIONS = new ArrayList<>();
static {
DEFAULT_SUB_ACTIONS.add(HandSide.LEFT.restrictToInputString);
DEFAULT_SUB_ACTIONS.add(HandSide.RIGHT.restrictToInputString);
}
private final ActionHandle actionHandle;
/**
* This is presented to the user as a description of the action. This string should be presented in the system’s current active locale.
*/
private final String translatedName;
private final ActionType actionType;
/**
* These are physical bindings to specific devices for this action.
*/
private final List suggestedBindings;
private final List supportedSubActionPaths;
public Action(ActionHandle actionHandle, String translatedName, ActionType actionType, List suggestedBindings) {
this.actionHandle = actionHandle;
this.translatedName = translatedName;
this.actionType = actionType;
this.suggestedBindings = suggestedBindings;
this.supportedSubActionPaths = DEFAULT_SUB_ACTIONS;
}
public Action(ActionHandle actionName, String translatedName, ActionType actionType, List suggestedBindings, List supportedSubActionPaths) {
this.actionHandle = actionName;
this.translatedName = translatedName;
this.actionType = actionType;
this.suggestedBindings = suggestedBindings;
this.supportedSubActionPaths = supportedSubActionPaths;
}
public String getActionName() {
return actionHandle.actionName();
}
public String getActionSetName() {
return actionHandle.actionSetName();
}
@SuppressWarnings("unused")
public static ActionBuilder builder() {
return new ActionBuilder();
}
@SuppressWarnings("unused")
public static class ActionBuilder {
private ActionHandle actionHandle;
private String translatedName;
private ActionType actionType;
/**
* These are physical bindings to specific devices for this action.
*/
private final List suggestedBindings = new ArrayList<>();
private List supportedSubActionPaths = DEFAULT_SUB_ACTIONS;
/**
* This is used to identify the action when you want to programatically interact with it e.g. getting an actions value. It is anticipated that
* these may be held in a static enum, or a static final field, or something similar where they can be easily accessed
* application wide.
*
* The action name should be things like "teleport", not things like "X Click". The idea is that they are
* abstract concept your application would like to support and they are bound to specific buttons based on the suggested
* bindings (which may be changed by the user, or guessed at by the binding).
*/
public ActionBuilder actionHandle(ActionHandle actionHandle) {
this.actionHandle = actionHandle;
return this;
}
/**
* This is presented to the user as a description of the action. This string should be presented in the system’s current active locale.
*/
public ActionBuilder translatedName(String translatedName) {
this.translatedName = translatedName;
return this;
}
public ActionBuilder actionType(ActionType actionType) {
this.actionType = actionType;
return this;
}
/**
* Suggested bindings are physical bindings to specific devices for this action.
* This method can be called multiple times to add more bindings (to the same action).
*
* At least one binding should be added for each profile (aka controller) you explicitly want to support.
*
* Intended usage is :
*
* withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().leftHand().haptic())
*
*
* Tamarin provided profiles are:
*
* - {@link GoogleDaydreamController#PROFILE}
* - {@link HtcProVive#PROFILE} - note this is the headset itself
* - {@link HtcViveController#PROFILE}
* - {@link KhronosSimpleController#PROFILE}
* - {@link MixedRealityMotionController#PROFILE}
* - {@link OculusGoController#PROFILE}
* - {@link OculusTouchController#PROFILE}
* - {@link ValveIndexController#PROFILE}
*
* @param profile the name of the controller (e.g. {@link OculusTouchController#PROFILE})
* @param binding the physics binding (e.g. `OculusTouchController.pathBuilder().leftHand().haptic()`)
*/
@SuppressWarnings("UnusedReturnValue")
public ActionBuilder withSuggestedBinding(String profile, String binding) {
this.suggestedBindings.add(new SuggestedBinding(profile, binding));
return this;
}
/**
* Suggested bindings are physical bindings to specific devices for this action.
* This method can be called multiple times to add more bindings.
*/
public ActionBuilder withSuggestedBinding(SuggestedBinding suggestedBinding) {
this.suggestedBindings.add(suggestedBinding);
return this;
}
/**
* Binds all the devices this library knows about (and that have haptics) to this action for both hands.
*
* Note that the occulus go does not have haptics so no binding is added for it
*/
public ActionBuilder withSuggestAllKnownHapticBindings() {
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().leftHand().haptic());
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().rightHand().haptic());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().leftHand().haptic());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().rightHand().haptic());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().leftHand().haptic());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().rightHand().haptic());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().leftHand().haptic());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().rightHand().haptic());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().leftHand().haptic());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().rightHand().haptic());
return this;
}
/**
* Binds all the devices this library knows about (and that have aim poses) to this action for both hands.
*
* Represents the position and orientation of the user's hand grip.
*
* This pose is typically derived from a hand-held controller's physical design and is generally aligned with the device's handle.
* In many cases, this pose aligns with the location of the user's hand when they are holding the device naturally.
*
* The orientation of this pose is typically as follows:
* - The X-axis points to the right of the handle.
* - The Y-axis points up along the handle.
* - The Z-axis points in the direction opposite to the front of the handle.
*
* This pose is commonly used for:
* - Placing virtual objects in the user's hand.
* - Representing the user's hand position in the virtual world.
*
* Note: The exact position and orientation can vary between devices. Always test with the specific device to ensure it behaves as expected.
*/
public ActionBuilder withSuggestAllKnownGripPoseBindings() {
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(OculusGoController.PROFILE, OculusGoController.pathBuilder().leftHand().gripPose());
withSuggestedBinding(OculusGoController.PROFILE, OculusGoController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(GoogleDaydreamController.PROFILE, GoogleDaydreamController.pathBuilder().rightHand().gripPose());
withSuggestedBinding(GoogleDaydreamController.PROFILE, GoogleDaydreamController.pathBuilder().leftHand().gripPose());
return this;
}
/**
* Binds all the devices this library knows about (and that have aim poses) to this action for both hands.
*
* Represents the position and orientation of the user's aiming direction.
*
* This pose is typically derived from a hand-held controller's physical design and is generally aligned with the device's primary pointing direction.
* In many cases, this pose aligns with the direction the user is pointing the device, like a laser pointer.
*
* The orientation of this pose is typically as follows:
* - The X-axis points to the right of the controller (as viewed from the back of the controller).
* - The Y-axis points up from the top of the controller.
* - The Z-axis points in the direction the user is pointing the controller (i.e., from the back of the controller towards the front).
*
* This pose is commonly used for:
* - Directing the user's gaze or aim in the virtual world.
* - Placing the origin of a raycast for selecting or interacting with virtual objects.
*
* Note: The exact position and orientation can vary between devices. Always test with the specific device to ensure it behaves as expected.
*/
public ActionBuilder withSuggestAllKnownAimPoseBindings() {
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(OculusTouchController.PROFILE, OculusTouchController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(HtcViveController.PROFILE, HtcViveController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(KhronosSimpleController.PROFILE, KhronosSimpleController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(MixedRealityMotionController.PROFILE, MixedRealityMotionController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(ValveIndexController.PROFILE, ValveIndexController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(OculusGoController.PROFILE, OculusGoController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(OculusGoController.PROFILE, OculusGoController.pathBuilder().rightHand().aimPose());
withSuggestedBinding(GoogleDaydreamController.PROFILE, GoogleDaydreamController.pathBuilder().leftHand().aimPose());
withSuggestedBinding(GoogleDaydreamController.PROFILE, GoogleDaydreamController.pathBuilder().rightHand().aimPose());
return this;
}
/**
* Sub action paths are things like "/user/hand/left", for use when restricting actions to a specific input source.
* This is defaulted to ["/user/hand/left", "/user/hand/right"] and there is usually no reason to change it.
*/
public void overrideSupportedSubActionPaths(List supportedSubActionPaths) {
this.supportedSubActionPaths = supportedSubActionPaths;
}
public Action build() {
if (actionHandle == null) {
throw new IllegalArgumentException("actionHandle cannot be null");
}
if (translatedName == null) {
translatedName = actionHandle.actionName();
}
if (actionType == null) {
throw new IllegalArgumentException("actionType cannot be null");
}
if (suggestedBindings.isEmpty()) {
throw new IllegalArgumentException("suggestedBindings cannot be empty");
}
return new Action(actionHandle, translatedName, actionType, suggestedBindings, supportedSubActionPaths);
}
}
/**
* This is presented to the user as a description of the action. This string should be presented in the system’s current active locale.
*/
public String getTranslatedName() {
return this.translatedName;
}
public ActionType getActionType() {
return this.actionType;
}
/**
* These are physical bindings to specific devices for this action.
*/
public List getSuggestedBindings() {
return this.suggestedBindings;
}
public List getSupportedSubActionPaths() {
return this.supportedSubActionPaths;
}
}