From 918c4d365b1c26707f3ed6b3e429c4ee9263b06b Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 26 Aug 2021 19:21:16 +0900 Subject: VR: Refactor motion capture objects Follows recent API changes. Motion capture objects are now presented as a UI list, with the added ability to set location/rotation offsets for objects relative to their target devices. --- viewport_vr_preview/__init__.py | 32 ++++---- viewport_vr_preview/main.py | 176 ++++++++++++++++++++++++++++++++++------ 2 files changed, 164 insertions(+), 44 deletions(-) diff --git a/viewport_vr_preview/__init__.py b/viewport_vr_preview/__init__.py index cb2a4c74..a70dd847 100644 --- a/viewport_vr_preview/__init__.py +++ b/viewport_vr_preview/__init__.py @@ -94,6 +94,14 @@ classes = ( main.VIEW3D_OT_vr_actionbinding_copy, main.VIEW3D_OT_vr_actionbindings_clear, + main.VRMotionCaptureObject, + main.VIEW3D_UL_vr_mocap_objects, + main.VIEW3D_MT_vr_mocap_object_menu, + + main.VIEW3D_OT_vr_mocap_object_add, + main.VIEW3D_OT_vr_mocap_object_remove, + main.VIEW3D_OT_vr_mocap_object_help, + main.VIEW3D_GT_vr_camera_cone, main.VIEW3D_GT_vr_controller_grip, main.VIEW3D_GT_vr_controller_aim, @@ -133,20 +141,12 @@ def register(): description="Enable bindings for the HP Reverb G2 controllers. Note that this may not be supported by all OpenXR runtimes", default=False, ) - bpy.types.Scene.vr_headset_object = bpy.props.PointerProperty( - name="Headset Object", - type=bpy.types.Object, - update=main.vr_headset_object_update, - ) - bpy.types.Scene.vr_controller0_object = bpy.props.PointerProperty( - name="Controller 0 Object", - type=bpy.types.Object, - update=main.vr_controller0_object_update, - ) - bpy.types.Scene.vr_controller1_object = bpy.props.PointerProperty( - name="Controller 1 Object", - type=bpy.types.Object, - update=main.vr_controller1_object_update, + # This scene collection property is needed instead of directly accessing + # XrSessionSettings.mocap_objects in the UI to avoid invalid pointers when + # deleting objects. + bpy.types.Scene.vr_mocap_objects = bpy.props.CollectionProperty( + name="Motion Capture Object", + type=main.VRMotionCaptureObject, ) # View3DShading is the only per 3D-View struct with custom property # support, so "abusing" that to get a per 3D-View option. @@ -180,9 +180,7 @@ def unregister(): del bpy.types.Scene.vr_actions_enable_cosmos del bpy.types.Scene.vr_actions_enable_huawei del bpy.types.Scene.vr_actions_enable_reverb_g2 - del bpy.types.Scene.vr_headset_object - del bpy.types.Scene.vr_controller0_object - del bpy.types.Scene.vr_controller1_object + del bpy.types.Scene.vr_mocap_objects del bpy.types.View3DShading.vr_show_virtual_camera del bpy.types.View3DShading.vr_show_controllers del bpy.types.View3DShading.vr_show_landmarks diff --git a/viewport_vr_preview/main.py b/viewport_vr_preview/main.py index a53678bf..6ae16413 100644 --- a/viewport_vr_preview/main.py +++ b/viewport_vr_preview/main.py @@ -1441,19 +1441,69 @@ class VIEW3D_OT_vr_actionbindings_clear(Operator): ### Motion capture. -def vr_headset_object_update(self, context): - session_settings = context.window_manager.xr_session_settings - session_settings.headset_object = context.scene.vr_headset_object +def vr_mocap_object_selected_get(session_settings): + mocap_objects = session_settings.mocap_objects + return ( + None if (len(mocap_objects) < + 1) else mocap_objects[session_settings.selected_mocap_object] + ) -def vr_controller0_object_update(self, context): - session_settings = context.window_manager.xr_session_settings - session_settings.controller0_object = context.scene.vr_controller0_object +def vr_scene_mocap_object_selected_get(scene, session_settings): + mocap_objects = scene.vr_mocap_objects + return ( + None if (len(mocap_objects) < + 1) else mocap_objects[session_settings.selected_mocap_object] + ) -def vr_controller1_object_update(self, context): +def vr_scene_mocap_object_update(self, context): session_settings = context.window_manager.xr_session_settings - session_settings.controller1_object = context.scene.vr_controller1_object + mocap_ob = vr_mocap_object_selected_get(session_settings) + if not mocap_ob: + return + + scene = context.scene + scene_mocap_ob = vr_scene_mocap_object_selected_get(scene, session_settings) + if not scene_mocap_ob: + return + + # Check for duplicate object. + if scene_mocap_ob.object and session_settings.mocap_objects.find(scene_mocap_ob.object): + scene_mocap_ob.object = None + return + + mocap_ob.object = scene_mocap_ob.object + + +class VRMotionCaptureObject(PropertyGroup): + object: bpy.props.PointerProperty( + name="Object", + type=bpy.types.Object, + update=vr_scene_mocap_object_update, + ) + + +class VIEW3D_UL_vr_mocap_objects(UIList): + def draw_item(self, context, layout, _data, item, icon, _active_data, + _active_propname, index): + scene_mocap_ob = item + + layout.emboss = 'NONE' + + if scene_mocap_ob.object: + layout.prop(scene_mocap_ob.object, "name", text="") + else: + layout.label(icon='X') + + +class VIEW3D_MT_vr_mocap_object_menu(Menu): + bl_label = "Motion Capture Object Controls" + + def draw(self, _context): + layout = self.layout + + layout.operator("view3d.vr_mocap_object_help") class VIEW3D_PT_vr_motion_capture(Panel): @@ -1465,8 +1515,11 @@ class VIEW3D_PT_vr_motion_capture(Panel): def draw(self, context): layout = self.layout - scene = context.scene + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + session_settings = context.window_manager.xr_session_settings + scene = context.scene col = layout.column(align=True) col.label(icon='ERROR', text="Note:") @@ -1476,25 +1529,94 @@ class VIEW3D_PT_vr_motion_capture(Panel): layout.separator() row = layout.row() - row.label(text="Headset") - col = row.column() - col.prop(scene, "vr_headset_object", text="") - col.prop(session_settings, "headset_object_enable", text="Enable") - col.prop(session_settings, "headset_object_autokey", text="Auto Key") - - row = layout.row() - row.label(text="Controller 0") - col = row.column() - col.prop(scene, "vr_controller0_object", text="") - col.prop(session_settings, "controller0_object_enable", text="Enable") - col.prop(session_settings, "controller0_object_autokey", text="Auto Key") + row.template_list("VIEW3D_UL_vr_mocap_objects", "", scene, "vr_mocap_objects", + session_settings, "selected_mocap_object", rows=3) - row = layout.row() - row.label(text="Controller 1") - col = row.column() - col.prop(scene, "vr_controller1_object", text="") - col.prop(session_settings, "controller1_object_enable", text="Enable") - col.prop(session_settings, "controller1_object_autokey", text="Auto Key") + col = row.column(align=True) + col.operator("view3d.vr_mocap_object_add", icon='ADD', text="") + col.operator("view3d.vr_mocap_object_remove", icon='REMOVE', text="") + + col.menu("VIEW3D_MT_vr_mocap_object_menu", icon='DOWNARROW_HLT', text="") + + mocap_ob = vr_mocap_object_selected_get(session_settings) + scene_mocap_ob = vr_scene_mocap_object_selected_get(scene, session_settings) + + if mocap_ob and scene_mocap_ob: + row = layout.row() + col = row.column(align=True) + + col.prop(scene_mocap_ob, "object", text="Object") + col.prop(mocap_ob, "user_path", text="User Path") + col.prop(mocap_ob, "enable", text="Enable") + col.prop(mocap_ob, "autokey", text="Auto Key") + col.prop(mocap_ob, "location_offset", text="Location Offset") + col.prop(mocap_ob, "rotation_offset", text="Rotation Offset") + + +class VIEW3D_OT_vr_mocap_object_add(Operator): + bl_idname = "view3d.vr_mocap_object_add" + bl_label = "Add VR Motion Capture Object" + bl_description = "Add a new VR motion capture object" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + session_settings = context.window_manager.xr_session_settings + + mocap_ob = session_settings.mocap_objects.new(None) + if not mocap_ob: + return {'CANCELLED'} + + context.scene.vr_mocap_objects.add() + + # Select newly created object. + session_settings.selected_mocap_object = len(session_settings.mocap_objects) - 1 + + return {'FINISHED'} + + +class VIEW3D_OT_vr_mocap_object_remove(Operator): + bl_idname = "view3d.vr_mocap_object_remove" + bl_label = "Remove VR Motion Capture Object" + bl_description = "Delete the selected VR motion capture object" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + session_settings = context.window_manager.xr_session_settings + + mocap_ob = vr_mocap_object_selected_get(session_settings) + if not mocap_ob: + return {'CANCELLED'} + + context.scene.vr_mocap_objects.remove(session_settings.selected_mocap_object) + + session_settings.mocap_objects.remove(mocap_ob) + + return {'FINISHED'} + + +class VIEW3D_OT_vr_mocap_object_help(Operator): + bl_idname = "view3d.vr_mocap_object_help" + bl_label = "Help" + bl_description = "Display information about VR motion capture objects" + bl_options = {'REGISTER'} + + def execute(self, context): + info_header = "Common User Paths:" + info_headset = "Headset - /user/head" + info_left_controller = "Left Controller* - /user/hand/left" + info_right_controller = "Right Controller* - /user/hand/right" + info_note = "*Requires VR actions for controller poses" + + def draw(self, context): + self.layout.label(text=info_header) + self.layout.label(text=info_headset) + self.layout.label(text=info_left_controller) + self.layout.label(text=info_right_controller) + self.layout.label(text=info_note) + + context.window_manager.popup_menu(draw, title="Motion Capture Objects", icon='INFO') + + return {'FINISHED'} ### Viewport feedback. -- cgit v1.2.3