Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Kim <pk15950@gmail.com>2022-02-20 09:24:51 +0300
committerPeter Kim <pk15950@gmail.com>2022-02-20 09:24:51 +0300
commitb172b0292c6d046d2211e87d0558e5cc23ac9664 (patch)
tree866207a74184f195ecce985f18784d0b29d443d3
parentb0274e50da58bc1f0086794a16029ec6e2e9b927 (diff)
VR: Customizable Actions
https://developer.blender.org/D13421
-rw-r--r--viewport_vr_preview/action_map.py93
-rw-r--r--viewport_vr_preview/action_map_io.py12
-rw-r--r--viewport_vr_preview/defaults.py25
-rw-r--r--viewport_vr_preview/gui.py402
-rw-r--r--viewport_vr_preview/operators.py565
5 files changed, 1042 insertions, 55 deletions
diff --git a/viewport_vr_preview/action_map.py b/viewport_vr_preview/action_map.py
index 6c62c05d..a9728ec4 100644
--- a/viewport_vr_preview/action_map.py
+++ b/viewport_vr_preview/action_map.py
@@ -4,6 +4,7 @@
if "bpy" in locals():
import importlib
+ importlib.reload(action_map_io)
importlib.reload(defaults)
else:
from . import action_map_io, defaults
@@ -15,22 +16,65 @@ import importlib.util
import os.path
+def vr_actionmap_active_get(session_settings):
+ actionmaps = session_settings.actionmaps
+ return (
+ None if (len(actionmaps) <
+ 1) else actionmaps[session_settings.active_actionmap]
+ )
+
+
+def vr_actionmap_selected_get(session_settings):
+ actionmaps = session_settings.actionmaps
+ return (
+ None if (len(actionmaps) <
+ 1) else actionmaps[session_settings.selected_actionmap]
+ )
+
+
+def vr_actionmap_item_selected_get(am):
+ actionmap_items = am.actionmap_items
+ return (
+ None if (len(actionmap_items) <
+ 1) else actionmap_items[am.selected_item]
+ )
+
+
+def vr_actionmap_user_path_selected_get(ami):
+ user_paths = ami.user_paths
+ return (
+ None if (len(user_paths) <
+ 1) else user_paths[ami.selected_user_path]
+ )
+
+
+def vr_actionmap_binding_selected_get(ami):
+ actionmap_bindings = ami.bindings
+ return (
+ None if (len(actionmap_bindings) <
+ 1) else actionmap_bindings[ami.selected_binding]
+ )
+
+
+def vr_actionmap_component_path_selected_get(amb):
+ component_paths = amb.component_paths
+ return (
+ None if (len(component_paths) <
+ 1) else component_paths[amb.selected_component_path]
+ )
+
+
def vr_actionset_active_update(context):
session_state = context.window_manager.xr_session_state
- if not session_state or len(session_state.actionmaps) < 1:
+ if not session_state:
return
- scene = context.scene
-
- if scene.vr_actions_use_gamepad and session_state.actionmaps.find(session_state, defaults.VRDefaultActionmaps.GAMEPAD.value):
- session_state.active_action_set_set(context, defaults.VRDefaultActionmaps.GAMEPAD.value)
- else:
- # Use first action map.
- session_state.active_action_set_set(context, session_state.actionmaps[0].name)
+ session_settings = context.window_manager.xr_session_settings
+ am = vr_actionmap_active_get(session_settings)
+ if not am:
+ return
-
-def vr_actions_use_gamepad_update(self, context):
- vr_actionset_active_update(context)
+ session_state.active_action_set_set(context, am.name)
@persistent
@@ -45,11 +89,14 @@ def vr_create_actions(context: bpy.context):
if not scene.vr_actions_enable:
return
- # Ensure default action maps.
- if not defaults.vr_ensure_default_actionmaps(session_state):
- return
+ session_settings = context.window_manager.xr_session_settings
+
+ if (len(session_settings.actionmaps) < 1):
+ # Ensure default action maps.
+ if not defaults.vr_ensure_default_actionmaps(session_settings):
+ return
- for am in session_state.actionmaps:
+ for am in session_settings.actionmaps:
if len(am.actionmap_items) < 1:
continue
@@ -101,7 +148,7 @@ def vr_create_actions(context: bpy.context):
vr_actionset_active_update(context)
-def vr_load_actionmaps(session_state, filepath):
+def vr_load_actionmaps(session_settings, filepath):
if not os.path.exists(filepath):
return False
@@ -109,16 +156,16 @@ def vr_load_actionmaps(session_state, filepath):
file = importlib.util.module_from_spec(spec)
spec.loader.exec_module(file)
- action_map_io.actionconfig_init_from_data(session_state, file.actionconfig_data, file.actionconfig_version)
+ action_map_io.actionconfig_init_from_data(session_settings, file.actionconfig_data, file.actionconfig_version)
return True
-def vr_save_actionmaps(session_state, filepath, sort=False):
- action_map_io.actionconfig_export_as_data(session_state, filepath, sort=sort)
+def vr_save_actionmaps(session_settings, filepath, sort=False):
+ action_map_io.actionconfig_export_as_data(session_settings, filepath, sort=sort)
print("Saved XR actionmaps: " + filepath)
-
+
return True
@@ -128,11 +175,6 @@ def register():
description="Enable default VR controller actions, including controller poses and haptics",
default=True,
)
- bpy.types.Scene.vr_actions_use_gamepad = bpy.props.BoolProperty(
- description="Use input from gamepad (Microsoft Xbox Controller) instead of motion controllers",
- default=False,
- update=vr_actions_use_gamepad_update,
- )
bpy.types.Scene.vr_actions_enable_huawei = bpy.props.BoolProperty(
description="Enable bindings for the Huawei controllers. Note that this may not be supported by all OpenXR runtimes",
default=False,
@@ -155,7 +197,6 @@ def register():
def unregister():
del bpy.types.Scene.vr_actions_enable
- del bpy.types.Scene.vr_actions_use_gamepad
del bpy.types.Scene.vr_actions_enable_huawei
del bpy.types.Scene.vr_actions_enable_reverb_g2
del bpy.types.Scene.vr_actions_enable_vive_cosmos
diff --git a/viewport_vr_preview/action_map_io.py b/viewport_vr_preview/action_map_io.py
index c4e04581..809a2906 100644
--- a/viewport_vr_preview/action_map_io.py
+++ b/viewport_vr_preview/action_map_io.py
@@ -189,10 +189,10 @@ def amb_data_from_args(amb, args, type):
amb.pose_rotation.z = float(l[2])
-def actionconfig_export_as_data(session_state, filepath, *, sort=False):
+def actionconfig_export_as_data(session_settings, filepath, *, sort=False):
export_actionmaps = []
- for am in session_state.actionmaps:
+ for am in session_settings.actionmaps:
export_actionmaps.append(am)
if sort:
@@ -318,7 +318,7 @@ def actionmap_init_from_data(am, am_items):
actionmap_item_init_from_data(ami, ami_bindings)
-def actionconfig_init_from_data(session_state, actionconfig_data, actionconfig_version):
+def actionconfig_init_from_data(session_settings, actionconfig_data, actionconfig_version):
# Load data in the format defined above.
#
# Runs at load time, keep this fast!
@@ -327,7 +327,7 @@ def actionconfig_init_from_data(session_state, actionconfig_data, actionconfig_v
actionconfig_data = actionconfig_update(actionconfig_data, actionconfig_version)
for (am_name, am_content) in actionconfig_data:
- am = session_state.actionmaps.new(session_state, am_name, True)
+ am = session_settings.actionmaps.new(am_name, True)
am_items = am_content["items"]
# Check here instead of inside 'actionmap_init_from_data'
# because we want to allow both tuple & list types in that case.
@@ -338,9 +338,9 @@ def actionconfig_init_from_data(session_state, actionconfig_data, actionconfig_v
actionmap_init_from_data(am, am_items)
-def actionconfig_import_from_data(session_state, actionconfig_data, *, actionconfig_version=(0, 0, 0)):
+def actionconfig_import_from_data(session_settings, actionconfig_data, *, actionconfig_version=(0, 0, 0)):
# Load data in the format defined above.
#
# Runs at load time, keep this fast!
import bpy
- actionconfig_init_from_data(session_state, actionconfig_data, actionconfig_version)
+ actionconfig_init_from_data(session_settings, actionconfig_data, actionconfig_version)
diff --git a/viewport_vr_preview/defaults.py b/viewport_vr_preview/defaults.py
index fe253e10..8d2a38f4 100644
--- a/viewport_vr_preview/defaults.py
+++ b/viewport_vr_preview/defaults.py
@@ -9,7 +9,6 @@ else:
from . import action_map
import bpy
-from bpy.app.handlers import persistent
from enum import Enum
import math
import os.path
@@ -71,8 +70,8 @@ class VRDefaultActionprofiles(Enum):
WMR = "/interaction_profiles/microsoft/motion_controller"
-def vr_defaults_actionmap_add(session_state, name):
- am = session_state.actionmaps.new(session_state, name, True)
+def vr_defaults_actionmap_add(session_settings, name):
+ am = session_settings.actionmaps.new(name, True)
return am
@@ -186,8 +185,8 @@ def vr_defaults_haptic_actionbinding_add(ami,
return amb
-def vr_defaults_create_default(session_state):
- am = vr_defaults_actionmap_add(session_state,
+def vr_defaults_create_default(session_settings):
+ am = vr_defaults_actionmap_add(session_settings,
VRDefaultActionmaps.DEFAULT.value)
if not am:
return
@@ -1199,8 +1198,8 @@ def vr_defaults_create_default(session_state):
"/output/haptic"])
-def vr_defaults_create_default_gamepad(session_state):
- am = vr_defaults_actionmap_add(session_state,
+def vr_defaults_create_default_gamepad(session_settings):
+ am = vr_defaults_actionmap_add(session_settings,
VRDefaultActionmaps.GAMEPAD.value)
ami = vr_defaults_action_add(am,
@@ -1476,11 +1475,11 @@ def vr_get_default_config_path():
return os.path.join(filepath, "default.py")
-def vr_ensure_default_actionmaps(session_state):
+def vr_ensure_default_actionmaps(session_settings):
loaded = True
for name in VRDefaultActionmaps:
- if not session_state.actionmaps.find(session_state, name.value):
+ if not session_settings.actionmaps.find(name.value):
loaded = False
break
@@ -1492,11 +1491,11 @@ def vr_ensure_default_actionmaps(session_state):
if not os.path.exists(filepath):
# Create and save default action maps.
- vr_defaults_create_default(session_state)
- vr_defaults_create_default_gamepad(session_state)
+ vr_defaults_create_default(session_settings)
+ vr_defaults_create_default_gamepad(session_settings)
- action_map.vr_save_actionmaps(session_state, filepath, sort=False)
+ action_map.vr_save_actionmaps(session_settings, filepath, sort=False)
- loaded = action_map.vr_load_actionmaps(session_state, filepath)
+ loaded = action_map.vr_load_actionmaps(session_settings, filepath)
return loaded
diff --git a/viewport_vr_preview/gui.py b/viewport_vr_preview/gui.py
index 163cbd48..cfbbc4c1 100644
--- a/viewport_vr_preview/gui.py
+++ b/viewport_vr_preview/gui.py
@@ -4,9 +4,10 @@
if "bpy" in locals():
import importlib
+ importlib.reload(action_map)
importlib.reload(properties)
else:
- from . import properties
+ from . import action_map, properties
import bpy
from bpy.types import (
@@ -68,7 +69,6 @@ class VIEW3D_PT_vr_session_view(Panel):
col = layout.column(align=True, heading="Show")
col.prop(session_settings, "show_floor", text="Floor")
col.prop(session_settings, "show_annotation", text="Annotations")
-
col.prop(session_settings, "show_selection", text="Selection")
col.prop(session_settings, "show_controllers", text="Controllers")
col.prop(session_settings, "show_custom_overlays", text="Custom Overlays")
@@ -156,25 +156,392 @@ class VIEW3D_PT_vr_landmarks(Panel):
"base_scale", text="Scale")
-### View.
-class VIEW3D_PT_vr_actionmaps(Panel):
+### Actions.
+def vr_indented_layout(layout, level):
+ # Same as _indented_layout() from rna_keymap_ui.py.
+ indentpx = 16
+ if level == 0:
+ level = 0.0001 # Tweak so that a percentage of 0 won't split by half
+ indent = level * indentpx / bpy.context.region.width
+
+ split = layout.split(factor=indent)
+ col = split.column()
+ col = split.column()
+ return col
+
+
+def vr_draw_ami(ami, layout, level):
+ # Similar to draw_kmi() from rna_keymap_ui.py.
+ col = vr_indented_layout(layout, level)
+
+ if ami.op:
+ col = col.column(align=True)
+ box = col.box()
+ else:
+ box = col.column()
+
+ split = box.split()
+
+ # Header bar.
+ row = split.row(align=True)
+ #row.prop(ami, "show_expanded", text="", emboss=False)
+
+ row.label(text="Operator Properties")
+ row.label(text=ami.op_name)
+
+ # Expanded, additional event settings.
+ if ami.op:
+ box = col.box()
+
+ # Operator properties.
+ box.template_xr_actionmap_item_properties(ami)
+
+
+class VIEW3D_UL_vr_actionmaps(UIList):
+ def draw_item(self, context, layout, _data, item, icon, _active_data,
+ _active_propname, index):
+ session_settings = context.window_manager.xr_session_settings
+
+ am_active_idx = session_settings.active_actionmap
+ am = item
+
+ layout.emboss = 'NONE'
+
+ layout.prop(am, "name", text="")
+
+ icon = (
+ 'RADIOBUT_ON' if (index == am_active_idx) else 'RADIOBUT_OFF'
+ )
+ props = layout.operator(
+ "view3d.vr_actionmap_activate", text="", icon=icon)
+ props.index = index
+
+
+class VIEW3D_MT_vr_actionmap_menu(Menu):
+ bl_label = "Action Map Controls"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("view3d.vr_actionmaps_defaults_load")
+ layout.operator("view3d.vr_actionmaps_import")
+ layout.operator("view3d.vr_actionmaps_export")
+ layout.operator("view3d.vr_actionmap_copy")
+ layout.operator("view3d.vr_actionmaps_clear")
+
+
+class VIEW3D_UL_vr_actions(UIList):
+ def draw_item(self, context, layout, _data, item, icon, _active_data,
+ _active_propname, index):
+ action = item
+
+ layout.emboss = 'NONE'
+
+ layout.prop(action, "name", text="")
+
+
+class VIEW3D_MT_vr_action_menu(Menu):
+ bl_label = "Action Controls"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("view3d.vr_action_copy")
+ layout.operator("view3d.vr_actions_clear")
+
+
+class VIEW3D_UL_vr_action_user_paths(UIList):
+ def draw_item(self, context, layout, _data, item, icon, _active_data,
+ _active_propname, index):
+ user_path = item
+
+ layout.emboss = 'NONE'
+
+ layout.prop(user_path, "path", text="")
+
+
+class VIEW3D_MT_vr_action_user_path_menu(Menu):
+ bl_label = "User Path Controls"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("view3d.vr_action_user_paths_clear")
+
+
+class VIEW3D_UL_vr_actionbindings(UIList):
+ def draw_item(self, context, layout, _data, item, icon, _active_data,
+ _active_propname, index):
+ amb = item
+
+ layout.emboss = 'NONE'
+
+ layout.prop(amb, "name", text="")
+
+
+class VIEW3D_MT_vr_actionbinding_menu(Menu):
+ bl_label = "Action Binding Controls"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("view3d.vr_actionbinding_copy")
+ layout.operator("view3d.vr_actionbindings_clear")
+
+
+class VIEW3D_UL_vr_actionbinding_component_paths(UIList):
+ def draw_item(self, context, layout, _data, item, icon, _active_data,
+ _active_propname, index):
+ component_path = item
+
+ layout.emboss = 'NONE'
+
+ layout.prop(component_path, "path", text="")
+
+
+class VIEW3D_MT_vr_actionbinding_component_path_menu(Menu):
+ bl_label = "Component Path Controls"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("view3d.vr_actionbinding_component_paths_clear")
+
+
+class VRActionsPanel:
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "VR"
- bl_label = "Action Maps"
bl_options = {'DEFAULT_CLOSED'}
+
+class VIEW3D_PT_vr_actions_actionmaps(VRActionsPanel, Panel):
+ bl_label = "Action Maps"
+
def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ scene = context.scene
+
layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ row = layout.row()
+ row.template_list("VIEW3D_UL_vr_actionmaps", "", session_settings, "actionmaps",
+ session_settings, "selected_actionmap", rows=3)
+
+ col = row.column(align=True)
+ col.operator("view3d.vr_actionmap_add", icon='ADD', text="")
+ col.operator("view3d.vr_actionmap_remove", icon='REMOVE', text="")
+
+ col.menu("VIEW3D_MT_vr_actionmap_menu", icon='DOWNARROW_HLT', text="")
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ row = layout.row()
+ col = row.column(align=True)
+
+ col.prop(am, "name", text="Action Map")
+
+
+class VIEW3D_PT_vr_actions_actions(VRActionsPanel, Panel):
+ bl_label = "Actions"
+ bl_parent_id = "VIEW3D_PT_vr_actions_actionmaps"
+
+ def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ col = vr_indented_layout(layout, 1)
+ row = col.row()
+ row.template_list("VIEW3D_UL_vr_actions", "", am, "actionmap_items",
+ am, "selected_item", rows=3)
+
+ col = row.column(align=True)
+ col.operator("view3d.vr_action_add", icon='ADD', text="")
+ col.operator("view3d.vr_action_remove", icon='REMOVE', text="")
+
+ col.menu("VIEW3D_MT_vr_action_menu", icon='DOWNARROW_HLT', text="")
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+
+ if ami:
+ row = layout.row()
+ col = row.column(align=True)
+
+ col.prop(ami, "name", text="Action")
+ col.prop(ami, "type", text="Type")
+
+ if ami.type == 'FLOAT' or ami.type == 'VECTOR2D':
+ col.prop(ami, "op", text="Operator")
+ col.prop(ami, "op_mode", text="Operator Mode")
+ col.prop(ami, "bimanual", text="Bimanual")
+ # Properties.
+ vr_draw_ami(ami, col, 1)
+ elif ami.type == 'POSE':
+ col.prop(ami, "pose_is_controller_grip", text="Use for Controller Grips")
+ col.prop(ami, "pose_is_controller_aim", text="Use for Controller Aims")
+
+
+class VIEW3D_PT_vr_actions_user_paths(VRActionsPanel, Panel):
+ bl_label = "User Paths"
+ bl_parent_id = "VIEW3D_PT_vr_actions_actions"
+
+ def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ ami = action_map.vr_actionmap_item_selected_get(am)
+
+ if ami:
+ col = vr_indented_layout(layout, 2)
+ row = col.row()
+ row.template_list("VIEW3D_UL_vr_action_user_paths", "", ami, "user_paths",
+ ami, "selected_user_path", rows=2)
+
+ col = row.column(align=True)
+ col.operator("view3d.vr_action_user_path_add", icon='ADD', text="")
+ col.operator("view3d.vr_action_user_path_remove", icon='REMOVE', text="")
+
+ col.menu("VIEW3D_MT_vr_action_user_path_menu", icon='DOWNARROW_HLT', text="")
+
+
+class VIEW3D_PT_vr_actions_haptics(VRActionsPanel, Panel):
+ bl_label = "Haptics"
+ bl_parent_id = "VIEW3D_PT_vr_actions_actions"
+
+ def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ ami = action_map.vr_actionmap_item_selected_get(am)
+
+ if ami:
+ row = layout.row()
+ col = row.column(align=True)
+
+ if ami.type == 'FLOAT' or ami.type == 'VECTOR2D':
+ col.prop(ami, "haptic_name", text="Haptic Action")
+ col.prop(ami, "haptic_match_user_paths", text="Match User Paths")
+ col.prop(ami, "haptic_duration", text="Duration")
+ col.prop(ami, "haptic_frequency", text="Frequency")
+ col.prop(ami, "haptic_amplitude", text="Amplitude")
+ col.prop(ami, "haptic_mode", text="Haptic Mode")
+
+
+class VIEW3D_PT_vr_actions_bindings(VRActionsPanel, Panel):
+ bl_label = "Bindings"
+ bl_parent_id = "VIEW3D_PT_vr_actions_actions"
+
+ def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ ami = action_map.vr_actionmap_item_selected_get(am)
+
+ if ami:
+ col = vr_indented_layout(layout, 2)
+ row = col.row()
+ row.template_list("VIEW3D_UL_vr_actionbindings", "", ami, "bindings",
+ ami, "selected_binding", rows=3)
+
+ col = row.column(align=True)
+ col.operator("view3d.vr_actionbinding_add", icon='ADD', text="")
+ col.operator("view3d.vr_actionbinding_remove", icon='REMOVE', text="")
+
+ col.menu("VIEW3D_MT_vr_actionbinding_menu", icon='DOWNARROW_HLT', text="")
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+
+ if amb:
+ row = layout.row()
+ col = row.column(align=True)
+
+ col.prop(amb, "name", text="Binding")
+ col.prop(amb, "profile", text="Profile")
+
+ if ami.type == 'FLOAT' or ami.type == 'VECTOR2D':
+ col.prop(amb, "threshold", text="Threshold")
+ if ami.type == 'FLOAT':
+ col.prop(amb, "axis0_region", text="Axis Region")
+ else: # ami.type == 'VECTOR2D'
+ col.prop(amb, "axis0_region", text="Axis 0 Region")
+ col.prop(amb, "axis1_region", text="Axis 1 Region")
+ elif ami.type == 'POSE':
+ col.prop(amb, "pose_location", text="Location Offset")
+ col.prop(amb, "pose_rotation", text="Rotation Offset")
+
+
+class VIEW3D_PT_vr_actions_component_paths(VRActionsPanel, Panel):
+ bl_label = "Component Paths"
+ bl_parent_id = "VIEW3D_PT_vr_actions_bindings"
+
+ def draw(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+
+ if am:
+ ami = action_map.vr_actionmap_item_selected_get(am)
+
+ if ami:
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+
+ if amb:
+ col = vr_indented_layout(layout, 3)
+ row = col.row()
+ row.template_list("VIEW3D_UL_vr_actionbinding_component_paths", "", amb, "component_paths",
+ amb, "selected_component_path", rows=2)
+
+ col = row.column(align=True)
+ col.operator("view3d.vr_actionbinding_component_path_add", icon='ADD', text="")
+ col.operator("view3d.vr_actionbinding_component_path_remove", icon='REMOVE', text="")
+
+ col.menu("VIEW3D_MT_vr_actionbinding_component_path_menu", icon='DOWNARROW_HLT', text="")
+
+
+class VIEW3D_PT_vr_actions_extensions(VRActionsPanel, Panel):
+ bl_label = "Extensions"
+ bl_parent_id = "VIEW3D_PT_vr_actions_actionmaps"
+
+ def draw(self, context):
scene = context.scene
+ layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
col = layout.column(align=True)
- col.prop(scene, "vr_actions_use_gamepad", text="Gamepad")
-
- col = layout.column(align=True, heading="Extensions")
col.prop(scene, "vr_actions_enable_reverb_g2", text="HP Reverb G2")
col.prop(scene, "vr_actions_enable_vive_cosmos", text="HTC Vive Cosmos")
col.prop(scene, "vr_actions_enable_vive_focus", text="HTC Vive Focus")
@@ -228,11 +595,28 @@ classes = (
VIEW3D_PT_vr_session,
VIEW3D_PT_vr_session_view,
VIEW3D_PT_vr_landmarks,
- VIEW3D_PT_vr_actionmaps,
+ VIEW3D_PT_vr_actions_actionmaps,
+ VIEW3D_PT_vr_actions_actions,
+ VIEW3D_PT_vr_actions_user_paths,
+ VIEW3D_PT_vr_actions_haptics,
+ VIEW3D_PT_vr_actions_bindings,
+ VIEW3D_PT_vr_actions_component_paths,
+ VIEW3D_PT_vr_actions_extensions,
VIEW3D_PT_vr_viewport_feedback,
VIEW3D_UL_vr_landmarks,
VIEW3D_MT_vr_landmark_menu,
+
+ VIEW3D_UL_vr_actionmaps,
+ VIEW3D_MT_vr_actionmap_menu,
+ VIEW3D_UL_vr_actions,
+ VIEW3D_MT_vr_action_menu,
+ VIEW3D_UL_vr_action_user_paths,
+ VIEW3D_MT_vr_action_user_path_menu,
+ VIEW3D_UL_vr_actionbindings,
+ VIEW3D_MT_vr_actionbinding_menu,
+ VIEW3D_UL_vr_actionbinding_component_paths,
+ VIEW3D_MT_vr_actionbinding_component_path_menu,
)
diff --git a/viewport_vr_preview/operators.py b/viewport_vr_preview/operators.py
index 69100762..d540658d 100644
--- a/viewport_vr_preview/operators.py
+++ b/viewport_vr_preview/operators.py
@@ -4,9 +4,11 @@
if "bpy" in locals():
import importlib
+ importlib.reload(action_map)
+ importlib.reload(defaults)
importlib.reload(properties)
else:
- from . import properties
+ from . import action_map, defaults, properties
import bpy
from bpy.types import (
@@ -14,10 +16,12 @@ from bpy.types import (
GizmoGroup,
Operator,
)
+from bpy_extras.io_utils import ExportHelper, ImportHelper
import bgl
import math
from math import radians
from mathutils import Euler, Matrix, Quaternion, Vector
+import os.path
### Landmarks.
@@ -241,6 +245,542 @@ class VIEW3D_OT_vr_landmark_activate(Operator):
return {'FINISHED'}
+ ### Actions.
+class VIEW3D_OT_vr_actionmap_add(Operator):
+ bl_idname = "view3d.vr_actionmap_add"
+ bl_label = "Add VR Action Map"
+ bl_description = "Add a new VR action map to the scene"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = session_settings.actionmaps.new("actionmap", False)
+ if not am:
+ return {'CANCELLED'}
+
+ # Select newly created actionmap.
+ session_settings.selected_actionmap = len(session_settings.actionmaps) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmap_remove(Operator):
+ bl_idname = "view3d.vr_actionmap_remove"
+ bl_label = "Remove VR Action Map"
+ bl_description = "Delete the selected VR action map from the scene"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ session_settings.actionmaps.remove(am)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmap_activate(Operator):
+ bl_idname = "view3d.vr_actionmap_activate"
+ bl_label = "Activate VR Action Map"
+ bl_description = "Set the current VR action map for the session"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ index: bpy.props.IntProperty(
+ name="Index",
+ options={'HIDDEN'},
+ )
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+ if (self.index >= len(session_settings.actionmaps)):
+ return {'CANCELLED'}
+
+ session_settings.active_actionmap = (
+ self.index if self.properties.is_property_set(
+ "index") else session_settings.selected_actionmap
+ )
+
+ action_map.vr_actionset_active_update(context)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmaps_defaults_load(Operator):
+ bl_idname = "view3d.vr_actionmaps_defaults_load"
+ bl_label = "Load Default VR Action Maps"
+ bl_description = "Load default VR action maps"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ filepath = defaults.vr_get_default_config_path()
+
+ if not action_map.vr_load_actionmaps(session_settings, filepath):
+ return {'CANCELLED'}
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmaps_import(Operator, ImportHelper):
+ bl_idname = "view3d.vr_actionmaps_import"
+ bl_label = "Import VR Action Maps"
+ bl_description = "Import VR action maps from configuration file"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ filter_glob: bpy.props.StringProperty(
+ default='*.py',
+ options={'HIDDEN'},
+ )
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ filename, ext = os.path.splitext(self.filepath)
+ if (ext != ".py"):
+ return {'CANCELLED'}
+
+ if not action_map.vr_load_actionmaps(session_settings, self.filepath):
+ return {'CANCELLED'}
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmaps_export(Operator, ExportHelper):
+ bl_idname = "view3d.vr_actionmaps_export"
+ bl_label = "Export VR Action Maps"
+ bl_description = "Export VR action maps to configuration file"
+ bl_options = {'REGISTER'}
+
+ filter_glob: bpy.props.StringProperty(
+ default='*.py',
+ options={'HIDDEN'},
+ )
+ filename_ext: bpy.props.StringProperty(
+ default='.py',
+ options={'HIDDEN'},
+ )
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ filename, ext = os.path.splitext(self.filepath)
+ if (ext != ".py"):
+ return {'CANCELLED'}
+
+ if not action_map.vr_save_actionmaps(session_settings, self.filepath):
+ return {'CANCELLED'}
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmap_copy(Operator):
+ bl_idname = "view3d.vr_actionmap_copy"
+ bl_label = "Copy VR Action Map"
+ bl_description = "Copy selected VR action map"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ # Copy actionmap.
+ am_new = session_settings.actionmaps.new_from_actionmap(am)
+ if not am_new:
+ return {'CANCELLED'}
+
+ # Select newly created actionmap.
+ session_settings.selected_actionmap = len(session_settings.actionmaps) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionmaps_clear(Operator):
+ bl_idname = "view3d.vr_actionmaps_clear"
+ bl_label = "Clear VR Action Maps"
+ bl_description = "Delete all VR action maps from the scene"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ while session_settings.actionmaps:
+ session_settings.actionmaps.remove(session_settings.actionmaps[0])
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_add(Operator):
+ bl_idname = "view3d.vr_action_add"
+ bl_label = "Add VR Action"
+ bl_description = "Add a new VR action to the action map"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = am.actionmap_items.new("action", False)
+ if not ami:
+ return {'CANCELLED'}
+
+ # Select newly created item.
+ am.selected_item = len(am.actionmap_items) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_remove(Operator):
+ bl_idname = "view3d.vr_action_remove"
+ bl_label = "Remove VR Action"
+ bl_description = "Delete the selected VR action from the action map"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ am.actionmap_items.remove(ami)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_copy(Operator):
+ bl_idname = "view3d.vr_action_copy"
+ bl_label = "Copy VR Action"
+ bl_description = "Copy selected VR action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ # Copy item.
+ ami_new = am.actionmap_items.new_from_item(ami)
+ if not ami_new:
+ return {'CANCELLED'}
+
+ # Select newly created item.
+ am.selected_item = len(am.actionmap_items) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actions_clear(Operator):
+ bl_idname = "view3d.vr_actions_clear"
+ bl_label = "Clear VR Actions"
+ bl_description = "Delete all VR actions from the action map"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ while am.actionmap_items:
+ am.actionmap_items.remove(am.actionmap_items[0])
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_user_path_add(Operator):
+ bl_idname = "view3d.vr_action_user_path_add"
+ bl_label = "Add User Path"
+ bl_description = "Add a new user path to the VR action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ user_path = ami.user_paths.new("/")
+ if not user_path:
+ return {'CANCELLED'}
+
+ # Select newly created user path.
+ ami.selected_user_path = len(ami.user_paths) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_user_path_remove(Operator):
+ bl_idname = "view3d.vr_action_user_path_remove"
+ bl_label = "Remove User Path"
+ bl_description = "Delete the selected user path from the VR action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ user_path = action_map.vr_actionmap_user_path_selected_get(ami)
+ if not user_path:
+ return {'CANCELLED'}
+
+ ami.user_paths.remove(user_path)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_action_user_paths_clear(Operator):
+ bl_idname = "view3d.vr_action_user_paths_clear"
+ bl_label = "Clear User Paths"
+ bl_description = "Delete all user paths from the VR action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ while ami.user_paths:
+ ami.user_paths.remove(ami.user_paths[0])
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_add(Operator):
+ bl_idname = "view3d.vr_actionbinding_add"
+ bl_label = "Add VR Action Binding"
+ bl_description = "Add a new VR action binding to the action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = ami.bindings.new("binding", False)
+ if not amb:
+ return {'CANCELLED'}
+
+ # Select newly created binding.
+ ami.selected_binding = len(ami.bindings) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_remove(Operator):
+ bl_idname = "view3d.vr_actionbinding_remove"
+ bl_label = "Remove VR Action Binding"
+ bl_description = "Delete the selected VR action binding from the action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+ if not amb:
+ return {'CANCELLED'}
+
+ ami.bindings.remove(amb)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_copy(Operator):
+ bl_idname = "view3d.vr_actionbinding_copy"
+ bl_label = "Copy VR Action Binding"
+ bl_description = "Copy selected VR action binding"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+ if not amb:
+ return {'CANCELLED'}
+
+ # Copy binding.
+ amb_new = ami.bindings.new_from_binding(amb)
+ if not amb_new:
+ return {'CANCELLED'}
+
+ # Select newly created binding.
+ ami.selected_binding = len(ami.bindings) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbindings_clear(Operator):
+ bl_idname = "view3d.vr_actionbindings_clear"
+ bl_label = "Clear VR Action Bindings"
+ bl_description = "Delete all VR action bindings from the action"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ while ami.bindings:
+ ami.bindings.remove(ami.bindings[0])
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_component_path_add(Operator):
+ bl_idname = "view3d.vr_actionbinding_component_path_add"
+ bl_label = "Add Component Path"
+ bl_description = "Add a new component path to the VR action binding"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+ if not amb:
+ return {'CANCELLED'}
+
+ component_path = amb.component_paths.new("/")
+ if not component_path:
+ return {'CANCELLED'}
+
+ # Select newly created component path.
+ amb.selected_component_path = len(amb.component_paths) - 1
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_component_path_remove(Operator):
+ bl_idname = "view3d.vr_actionbinding_component_path_remove"
+ bl_label = "Remove Component Path"
+ bl_description = "Delete the selected component path from the VR action binding"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+ if not amb:
+ return {'CANCELLED'}
+
+ component_path = action_map.vr_actionmap_component_path_selected_get(amb)
+ if not component_path:
+ return {'CANCELLED'}
+
+ amb.component_paths.remove(component_path)
+
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_vr_actionbinding_component_paths_clear(Operator):
+ bl_idname = "view3d.vr_actionbinding_component_paths_clear"
+ bl_label = "Clear Component Paths"
+ bl_description = "Delete all component paths from the VR action binding"
+ bl_options = {'UNDO', 'REGISTER'}
+
+ def execute(self, context):
+ session_settings = context.window_manager.xr_session_settings
+
+ am = action_map.vr_actionmap_selected_get(session_settings)
+ if not am:
+ return {'CANCELLED'}
+
+ ami = action_map.vr_actionmap_item_selected_get(am)
+ if not ami:
+ return {'CANCELLED'}
+
+ amb = action_map.vr_actionmap_binding_selected_get(ami)
+ if not amb:
+ return {'CANCELLED'}
+
+ while amb.component_paths:
+ amb.component_paths.remove(amb.component_paths[0])
+
+ return {'FINISHED'}
+
+
### Gizmos.
class VIEW3D_GT_vr_camera_cone(Gizmo):
bl_idname = "VIEW_3D_GT_vr_camera_cone"
@@ -486,6 +1026,29 @@ classes = (
VIEW3D_OT_cursor_to_vr_landmark,
VIEW3D_OT_update_vr_landmark,
+ VIEW3D_OT_vr_actionmap_add,
+ VIEW3D_OT_vr_actionmap_remove,
+ VIEW3D_OT_vr_actionmap_activate,
+ VIEW3D_OT_vr_actionmaps_defaults_load,
+ VIEW3D_OT_vr_actionmaps_import,
+ VIEW3D_OT_vr_actionmaps_export,
+ VIEW3D_OT_vr_actionmap_copy,
+ VIEW3D_OT_vr_actionmaps_clear,
+ VIEW3D_OT_vr_action_add,
+ VIEW3D_OT_vr_action_remove,
+ VIEW3D_OT_vr_action_copy,
+ VIEW3D_OT_vr_actions_clear,
+ VIEW3D_OT_vr_action_user_path_add,
+ VIEW3D_OT_vr_action_user_path_remove,
+ VIEW3D_OT_vr_action_user_paths_clear,
+ VIEW3D_OT_vr_actionbinding_add,
+ VIEW3D_OT_vr_actionbinding_remove,
+ VIEW3D_OT_vr_actionbinding_copy,
+ VIEW3D_OT_vr_actionbindings_clear,
+ VIEW3D_OT_vr_actionbinding_component_path_add,
+ VIEW3D_OT_vr_actionbinding_component_path_remove,
+ VIEW3D_OT_vr_actionbinding_component_paths_clear,
+
VIEW3D_GT_vr_camera_cone,
VIEW3D_GT_vr_controller_grip,
VIEW3D_GT_vr_controller_aim,