Add Xbox Controller Mapper node and update gamepad node documentation
This commit is contained in:
@@ -55,6 +55,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"VrchVideoWebViewerNode": VrchVideoWebViewerNode,
|
||||
"VrchWebSocketServerNode": VrchWebSocketServerNode,
|
||||
"VrchWebViewerNode": VrchWebViewerNode,
|
||||
"VrchXboxControllerNode": VrchXboxControllerNode,
|
||||
"VrchXYOSCControlNode": VrchXYOSCControlNode,
|
||||
"VrchXYZOSCControlNode": VrchXYZOSCControlNode,
|
||||
}
|
||||
@@ -103,6 +104,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"VrchVideoWebViewerNode": "VIDEO Web Viewer @ vrch.ai",
|
||||
"VrchWebSocketServerNode": "WebSocket Server @ vrch.ai",
|
||||
"VrchWebViewerNode": "Web Viewer @ vrch.ai",
|
||||
"VrchXboxControllerNode": "Xbox Controller Mapper @ vrch.ai",
|
||||
"VrchXYOSCControlNode": "XY OSC Control @ vrch.ai",
|
||||
"VrchXYZOSCControlNode": "XYZ OSC Control @ vrch.ai",
|
||||
}
|
||||
|
||||
@@ -42,3 +42,64 @@
|
||||
- Buttons 12-15: D-pad (Up/Down/Left/Right)
|
||||
- Axes 0-1: Left analog stick (X, Y)
|
||||
- Axes 2-3: Right analog stick (X, Y)
|
||||
|
||||
### Node: `Xbox Controller Mapper @ vrch.ai` (vrch.ai/control/gamepad)
|
||||
|
||||
1. **Overview:**
|
||||
- The `Xbox Controller Mapper @ vrch.ai` node provides optimized mapping for Xbox controllers.
|
||||
- It takes raw gamepad data from the `Gamepad Loader @ vrch.ai` node and maps it to standard Xbox controller inputs.
|
||||
- This makes it easier to work with Xbox controllers by providing named outputs for each button and control.
|
||||
|
||||
2. **Setup:**
|
||||
- First add a `Gamepad Loader @ vrch.ai` node to your workflow.
|
||||
- Then add the `Xbox Controller Mapper @ vrch.ai` node.
|
||||
- Connect the `RAW_DATA` output from the Gamepad Loader to the `raw_data` input of the Xbox Controller Mapper.
|
||||
|
||||
3. **Inputs:**
|
||||
- **`raw_data`**: JSON data from the `Gamepad Loader @ vrch.ai` node.
|
||||
- **`debug`**: Enable to view detailed debug information in the console. Default is `False`.
|
||||
|
||||
4. **Outputs:**
|
||||
- **Analog Controls:**
|
||||
- **`LEFT_STICK`**: Array containing X and Y values for the left analog stick `[x, y]` (values range from -1.0 to 1.0).
|
||||
- **`RIGHT_STICK`**: Array containing X and Y values for the right analog stick `[x, y]` (values range from -1.0 to 1.0).
|
||||
- **`LEFT_TRIGGER`**: Value for the left trigger (LT) (values range from 0.0 to 1.0).
|
||||
- **`RIGHT_TRIGGER`**: Value for the right trigger (RT) (values range from 0.0 to 1.0).
|
||||
|
||||
- **Face Buttons:**
|
||||
- **`A_BUTTON`**: Boolean state of the A button (bottom face button).
|
||||
- **`B_BUTTON`**: Boolean state of the B button (right face button).
|
||||
- **`X_BUTTON`**: Boolean state of the X button (left face button).
|
||||
- **`Y_BUTTON`**: Boolean state of the Y button (top face button).
|
||||
|
||||
- **Shoulder Buttons:**
|
||||
- **`LB_BUTTON`**: Boolean state of the left bumper (LB).
|
||||
- **`RB_BUTTON`**: Boolean state of the right bumper (RB).
|
||||
|
||||
- **Center Buttons:**
|
||||
- **`VIEW_BUTTON`**: Boolean state of the View button (formerly Back).
|
||||
- **`MENU_BUTTON`**: Boolean state of the Menu button (formerly Start).
|
||||
- **`XBOX_BUTTON`**: Boolean state of the Xbox logo button.
|
||||
|
||||
- **Stick Presses:**
|
||||
- **`LEFT_STICK_PRESS`**: Boolean state of the left stick press (L3).
|
||||
- **`RIGHT_STICK_PRESS`**: Boolean state of the right stick press (R3).
|
||||
|
||||
- **D-Pad:**
|
||||
- **`DPAD_UP`**: Boolean state of the D-pad up direction.
|
||||
- **`DPAD_DOWN`**: Boolean state of the D-pad down direction.
|
||||
- **`DPAD_LEFT`**: Boolean state of the D-pad left direction.
|
||||
- **`DPAD_RIGHT`**: Boolean state of the D-pad right direction.
|
||||
|
||||
- **Complete Data:**
|
||||
- **`FULL_MAPPING`**: Complete mapping of all controller inputs as a JSON object.
|
||||
|
||||
5. **Usage Examples:**
|
||||
- Use `A_BUTTON` to trigger actions in your workflow.
|
||||
- Use `LEFT_STICK` to control position or movement parameters.
|
||||
- Use `RIGHT_TRIGGER` to control intensity or pressure-sensitive parameters.
|
||||
|
||||
**Note:**
|
||||
- This node is specifically optimized for Xbox controllers and follows the standard Xbox controller button layout.
|
||||
- The button mapping is based on the standard Web Gamepad API implementation for Xbox controllers.
|
||||
- If your controller has a different layout, you may need to adjust your workflow accordingly.
|
||||
+329
-6
@@ -21,12 +21,12 @@ class VrchGamepadLoaderNode:
|
||||
}
|
||||
|
||||
RETURN_TYPES = (
|
||||
"JSON", # RAW_DATA
|
||||
"FLOAT", # LEFT_STICK
|
||||
"FLOAT", # RIGHT_STICK
|
||||
"BOOL", # BUTTONS_BOOLEAN
|
||||
"INT", # BUTTONS_INT
|
||||
"FLOAT", # BUTTONS_FLOAT
|
||||
"JSON", # RAW_DATA
|
||||
"FLOAT", # LEFT_STICK
|
||||
"FLOAT", # RIGHT_STICK
|
||||
"BOOLEAN", # BUTTONS_BOOLEAN
|
||||
"INT", # BUTTONS_INT
|
||||
"FLOAT", # BUTTONS_FLOAT
|
||||
)
|
||||
|
||||
RETURN_NAMES = (
|
||||
@@ -170,3 +170,326 @@ class VrchGamepadLoaderNode:
|
||||
m = hashlib.sha256()
|
||||
m.update(raw_data.encode("utf-8"))
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
class VrchXboxControllerNode:
|
||||
"""
|
||||
A specialized node for mapping Xbox controller inputs from raw gamepad data.
|
||||
This node takes raw gamepad data from VrchGamepadLoaderNode and maps it to
|
||||
standard Xbox controller buttons and controls for easier use in workflows.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
"""
|
||||
Define the input parameters for the node.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing input parameter specifications
|
||||
"""
|
||||
return {
|
||||
"required": {
|
||||
"raw_data": ("JSON", {"default": {}}), # Raw gamepad data from VrchGamepadLoaderNode
|
||||
"debug": ("BOOLEAN", {"default": False}), # Enable debug output
|
||||
},
|
||||
}
|
||||
|
||||
# Define the types of outputs this node will provide
|
||||
RETURN_TYPES = (
|
||||
"JSON", # FULL_MAPPING - Complete mapping as JSON
|
||||
"FLOAT", # LEFT_STICK - Array with [x, y] values
|
||||
"FLOAT", # RIGHT_STICK - Array with [x, y] values
|
||||
"FLOAT", # LEFT_TRIGGER - Value from 0.0 to 1.0
|
||||
"FLOAT", # RIGHT_TRIGGER - Value from 0.0 to 1.0
|
||||
"BOOLEAN", # A_BUTTON - Bottom face button
|
||||
"BOOLEAN", # B_BUTTON - Right face button
|
||||
"BOOLEAN", # X_BUTTON - Left face button
|
||||
"BOOLEAN", # Y_BUTTON - Top face button
|
||||
"BOOLEAN", # LB_BUTTON - Left bumper
|
||||
"BOOLEAN", # RB_BUTTON - Right bumper
|
||||
"BOOLEAN", # VIEW_BUTTON - Left center button (formerly Back)
|
||||
"BOOLEAN", # MENU_BUTTON - Right center button (formerly Start)
|
||||
"BOOLEAN", # LEFT_STICK_PRESS - Left stick click
|
||||
"BOOLEAN", # RIGHT_STICK_PRESS - Right stick click
|
||||
"BOOLEAN", # DPAD_UP - D-pad up direction
|
||||
"BOOLEAN", # DPAD_DOWN - D-pad down direction
|
||||
"BOOLEAN", # DPAD_LEFT - D-pad left direction
|
||||
"BOOLEAN", # DPAD_RIGHT - D-pad right direction
|
||||
"BOOLEAN", # XBOX_BUTTON - Xbox logo button
|
||||
)
|
||||
|
||||
# Names for the outputs, matching the types above
|
||||
RETURN_NAMES = (
|
||||
"FULL_MAPPING",
|
||||
"LEFT_STICK",
|
||||
"RIGHT_STICK",
|
||||
"LEFT_TRIGGER",
|
||||
"RIGHT_TRIGGER",
|
||||
"A_BUTTON",
|
||||
"B_BUTTON",
|
||||
"X_BUTTON",
|
||||
"Y_BUTTON",
|
||||
"LB_BUTTON",
|
||||
"RB_BUTTON",
|
||||
"VIEW_BUTTON",
|
||||
"MENU_BUTTON",
|
||||
"LEFT_STICK_PRESS",
|
||||
"RIGHT_STICK_PRESS",
|
||||
"DPAD_UP",
|
||||
"DPAD_DOWN",
|
||||
"DPAD_LEFT",
|
||||
"DPAD_RIGHT",
|
||||
"XBOX_BUTTON",
|
||||
)
|
||||
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "process_xbox_controller"
|
||||
|
||||
def process_xbox_controller(self, raw_data, debug=False):
|
||||
"""
|
||||
Process raw gamepad data and map it to Xbox controller inputs.
|
||||
|
||||
Args:
|
||||
raw_data (dict): Raw gamepad data from VrchGamepadLoaderNode
|
||||
debug (bool): Whether to print debug information
|
||||
|
||||
Returns:
|
||||
tuple: All mapped Xbox controller inputs as specified in RETURN_TYPES
|
||||
"""
|
||||
# Initialize default values for all outputs
|
||||
left_stick = [0.0, 0.0] # [x, y] values for left analog stick
|
||||
right_stick = [0.0, 0.0] # [x, y] values for right analog stick
|
||||
left_trigger = 0.0 # Value for left trigger (LT)
|
||||
right_trigger = 0.0 # Value for right trigger (RT)
|
||||
|
||||
# Initialize button states (all False by default)
|
||||
a_button = False # A button (bottom face button)
|
||||
b_button = False # B button (right face button)
|
||||
x_button = False # X button (left face button)
|
||||
y_button = False # Y button (top face button)
|
||||
lb_button = False # Left bumper (LB)
|
||||
rb_button = False # Right bumper (RB)
|
||||
view_button = False # View button (formerly Back)
|
||||
menu_button = False # Menu button (formerly Start)
|
||||
left_stick_press = False # Left stick press (L3)
|
||||
right_stick_press = False # Right stick press (R3)
|
||||
dpad_up = False # D-pad up
|
||||
dpad_down = False # D-pad down
|
||||
dpad_left = False # D-pad left
|
||||
dpad_right = False # D-pad right
|
||||
xbox_button = False # Xbox logo button
|
||||
|
||||
# Create a complete mapping object for the JSON output
|
||||
full_mapping = {
|
||||
"left_stick": left_stick,
|
||||
"right_stick": right_stick,
|
||||
"left_trigger": left_trigger,
|
||||
"right_trigger": right_trigger,
|
||||
"a_button": a_button,
|
||||
"b_button": b_button,
|
||||
"x_button": x_button,
|
||||
"y_button": y_button,
|
||||
"lb_button": lb_button,
|
||||
"rb_button": rb_button,
|
||||
"view_button": view_button,
|
||||
"menu_button": menu_button,
|
||||
"left_stick_press": left_stick_press,
|
||||
"right_stick_press": right_stick_press,
|
||||
"dpad_up": dpad_up,
|
||||
"dpad_down": dpad_down,
|
||||
"dpad_left": dpad_left,
|
||||
"dpad_right": dpad_right,
|
||||
"xbox_button": xbox_button,
|
||||
}
|
||||
|
||||
# If no data is provided, return default values
|
||||
if not raw_data:
|
||||
if debug:
|
||||
print("[VrchXboxControllerNode] No raw data provided.")
|
||||
return (
|
||||
left_stick, right_stick, left_trigger, right_trigger,
|
||||
a_button, b_button, x_button, y_button,
|
||||
lb_button, rb_button, view_button, menu_button,
|
||||
left_stick_press, right_stick_press,
|
||||
dpad_up, dpad_down, dpad_left, dpad_right,
|
||||
xbox_button, full_mapping
|
||||
)
|
||||
|
||||
# Parse and process the raw_data
|
||||
try:
|
||||
# Extract data from raw_data
|
||||
if "raw_data" in raw_data and raw_data["raw_data"]:
|
||||
# Handle cases where raw_data might be a JSON string
|
||||
if isinstance(raw_data["raw_data"], str):
|
||||
try:
|
||||
parsed_data = json.loads(raw_data["raw_data"])
|
||||
except json.JSONDecodeError:
|
||||
# If JSON parsing fails, use raw_data as is
|
||||
parsed_data = raw_data
|
||||
else:
|
||||
# If raw_data is already a dict, use it directly
|
||||
parsed_data = raw_data
|
||||
|
||||
# Process axes data (analog sticks)
|
||||
if "axes" in parsed_data and isinstance(parsed_data["axes"], list):
|
||||
axes = parsed_data["axes"]
|
||||
# Map axes to stick positions
|
||||
if len(axes) > 0:
|
||||
left_stick[0] = float(axes[0]) # Left stick X-axis
|
||||
if len(axes) > 1:
|
||||
left_stick[1] = float(axes[1]) # Left stick Y-axis
|
||||
if len(axes) > 2:
|
||||
right_stick[0] = float(axes[2]) # Right stick X-axis
|
||||
if len(axes) > 3:
|
||||
right_stick[1] = float(axes[3]) # Right stick Y-axis
|
||||
|
||||
# Process buttons data
|
||||
if "buttons" in parsed_data and isinstance(parsed_data["buttons"], list):
|
||||
buttons = parsed_data["buttons"]
|
||||
|
||||
# Map buttons to Xbox controller layout
|
||||
# Standard Xbox controller button mapping
|
||||
if len(buttons) > 0:
|
||||
a_button = self._get_button_state(buttons, 0) # A button
|
||||
if len(buttons) > 1:
|
||||
b_button = self._get_button_state(buttons, 1) # B button
|
||||
if len(buttons) > 2:
|
||||
x_button = self._get_button_state(buttons, 2) # X button
|
||||
if len(buttons) > 3:
|
||||
y_button = self._get_button_state(buttons, 3) # Y button
|
||||
if len(buttons) > 4:
|
||||
lb_button = self._get_button_state(buttons, 4) # Left bumper
|
||||
if len(buttons) > 5:
|
||||
rb_button = self._get_button_state(buttons, 5) # Right bumper
|
||||
if len(buttons) > 6:
|
||||
left_trigger = self._get_button_value(buttons, 6) # Left trigger
|
||||
if len(buttons) > 7:
|
||||
right_trigger = self._get_button_value(buttons, 7) # Right trigger
|
||||
if len(buttons) > 8:
|
||||
view_button = self._get_button_state(buttons, 8) # View button
|
||||
if len(buttons) > 9:
|
||||
menu_button = self._get_button_state(buttons, 9) # Menu button
|
||||
if len(buttons) > 10:
|
||||
left_stick_press = self._get_button_state(buttons, 10) # Left stick press
|
||||
if len(buttons) > 11:
|
||||
right_stick_press = self._get_button_state(buttons, 11) # Right stick press
|
||||
if len(buttons) > 12:
|
||||
dpad_up = self._get_button_state(buttons, 12) # D-pad up
|
||||
if len(buttons) > 13:
|
||||
dpad_down = self._get_button_state(buttons, 13) # D-pad down
|
||||
if len(buttons) > 14:
|
||||
dpad_left = self._get_button_state(buttons, 14) # D-pad left
|
||||
if len(buttons) > 15:
|
||||
dpad_right = self._get_button_state(buttons, 15) # D-pad right
|
||||
if len(buttons) > 16:
|
||||
xbox_button = self._get_button_state(buttons, 16) # Xbox button
|
||||
|
||||
# Update the full mapping object with current values
|
||||
full_mapping = {
|
||||
"left_stick": left_stick,
|
||||
"right_stick": right_stick,
|
||||
"left_trigger": left_trigger,
|
||||
"right_trigger": right_trigger,
|
||||
"a_button": a_button,
|
||||
"b_button": b_button,
|
||||
"x_button": x_button,
|
||||
"y_button": y_button,
|
||||
"lb_button": lb_button,
|
||||
"rb_button": rb_button,
|
||||
"view_button": view_button,
|
||||
"menu_button": menu_button,
|
||||
"left_stick_press": left_stick_press,
|
||||
"right_stick_press": right_stick_press,
|
||||
"dpad_up": dpad_up,
|
||||
"dpad_down": dpad_down,
|
||||
"dpad_left": dpad_left,
|
||||
"dpad_right": dpad_right,
|
||||
"xbox_button": xbox_button,
|
||||
}
|
||||
|
||||
# Print debug information if enabled
|
||||
if debug:
|
||||
print(f"[VrchXboxControllerNode] Xbox controller mapping: {full_mapping}")
|
||||
|
||||
# Return all mapped values
|
||||
return (
|
||||
full_mapping,
|
||||
left_stick, right_stick, left_trigger, right_trigger,
|
||||
a_button, b_button, x_button, y_button,
|
||||
lb_button, rb_button, view_button, menu_button,
|
||||
left_stick_press, right_stick_press,
|
||||
dpad_up, dpad_down, dpad_left, dpad_right,
|
||||
xbox_button,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Handle any errors during processing
|
||||
if debug:
|
||||
print(f"[VrchXboxControllerNode] Error processing Xbox controller data: {str(e)}")
|
||||
|
||||
# Return default values in case of error
|
||||
return (
|
||||
full_mapping,
|
||||
left_stick, right_stick, left_trigger, right_trigger,
|
||||
a_button, b_button, x_button, y_button,
|
||||
lb_button, rb_button, view_button, menu_button,
|
||||
left_stick_press, right_stick_press,
|
||||
dpad_up, dpad_down, dpad_left, dpad_right,
|
||||
xbox_button,
|
||||
)
|
||||
|
||||
def _get_button_state(self, buttons, index):
|
||||
"""
|
||||
Get the pressed state of a button at the specified index.
|
||||
|
||||
Args:
|
||||
buttons (list): List of button objects
|
||||
index (int): Index of the button to check
|
||||
|
||||
Returns:
|
||||
bool: True if the button is pressed, False otherwise
|
||||
"""
|
||||
try:
|
||||
btn = buttons[index]
|
||||
if isinstance(btn, dict):
|
||||
return bool(btn.get("pressed", False))
|
||||
elif hasattr(btn, "pressed"):
|
||||
return bool(btn.pressed)
|
||||
return False
|
||||
except (IndexError, TypeError):
|
||||
return False
|
||||
|
||||
def _get_button_value(self, buttons, index):
|
||||
"""
|
||||
Get the analog value of a button at the specified index.
|
||||
Used for triggers which can have values between 0.0 and 1.0.
|
||||
|
||||
Args:
|
||||
buttons (list): List of button objects
|
||||
index (int): Index of the button to check
|
||||
|
||||
Returns:
|
||||
float: Value of the button (0.0 to 1.0)
|
||||
"""
|
||||
try:
|
||||
btn = buttons[index]
|
||||
if isinstance(btn, dict):
|
||||
return float(btn.get("value", 0.0))
|
||||
elif hasattr(btn, "value"):
|
||||
return float(btn.value)
|
||||
return 0.0
|
||||
except (IndexError, TypeError):
|
||||
return 0.0
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, **kwargs):
|
||||
raw_data = kwargs.get("raw_data", "")
|
||||
debug = kwargs.get("debug", False)
|
||||
if not raw_data:
|
||||
if debug:
|
||||
print("[VrchXboxControllerNode] No raw_data provided to IS_CHANGED.")
|
||||
return False
|
||||
|
||||
m = hashlib.sha256()
|
||||
m.update(raw_data.encode("utf-8"))
|
||||
return m.hexdigest()
|
||||
|
||||
Reference in New Issue
Block a user