diff options
-rw-r--r-- | viewport_vr_preview.py | 840 | ||||
-rw-r--r-- | viewport_vr_preview/__init__.py | 68 | ||||
-rw-r--r-- | viewport_vr_preview/action_map.py | 172 | ||||
-rw-r--r-- | viewport_vr_preview/action_map_io.py | 348 | ||||
-rw-r--r-- | viewport_vr_preview/configs/default.py | 406 | ||||
-rw-r--r-- | viewport_vr_preview/defaults.py | 1518 | ||||
-rw-r--r-- | viewport_vr_preview/gui.py | 277 | ||||
-rw-r--r-- | viewport_vr_preview/operators.py | 521 | ||||
-rw-r--r-- | viewport_vr_preview/properties.py | 244 | ||||
-rw-r--r-- | viewport_vr_preview/versioning.py | 54 |
10 files changed, 3608 insertions, 840 deletions
diff --git a/viewport_vr_preview.py b/viewport_vr_preview.py deleted file mode 100644 index 07f34756..00000000 --- a/viewport_vr_preview.py +++ /dev/null @@ -1,840 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -import bpy -from bpy.types import ( - Gizmo, - GizmoGroup, - PropertyGroup, - UIList, - Menu, - Panel, - Operator, -) -from bpy.props import ( - CollectionProperty, - IntProperty, - BoolProperty, -) -from bpy.app.handlers import persistent - -bl_info = { - "name": "VR Scene Inspection", - "author": "Julian Eisel (Severin), Sebastian Koenig", - "version": (0, 9, 0), - "blender": (2, 90, 0), - "location": "3D View > Sidebar > VR", - "description": ("View the viewport with virtual reality glasses " - "(head-mounted displays)"), - "support": "OFFICIAL", - "warning": "This is an early, limited preview of in development " - "VR support for Blender.", - "doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/vr_scene_inspection.html", - "category": "3D View", -} - - -@persistent -def ensure_default_vr_landmark(context: bpy.context): - # Ensure there's a default landmark (scene camera by default). - landmarks = bpy.context.scene.vr_landmarks - if not landmarks: - landmarks.add() - landmarks[0].type = 'SCENE_CAMERA' - - -def xr_landmark_active_type_update(self, context): - wm = context.window_manager - session_settings = wm.xr_session_settings - landmark_active = VRLandmark.get_active_landmark(context) - - # Update session's base pose type to the matching type. - if landmark_active.type == 'SCENE_CAMERA': - session_settings.base_pose_type = 'SCENE_CAMERA' - elif landmark_active.type == 'USER_CAMERA': - session_settings.base_pose_type = 'OBJECT' - elif landmark_active.type == 'CUSTOM': - session_settings.base_pose_type = 'CUSTOM' - - -def xr_landmark_active_camera_update(self, context): - session_settings = context.window_manager.xr_session_settings - landmark_active = VRLandmark.get_active_landmark(context) - - # Update the anchor object to the (new) camera of this landmark. - session_settings.base_pose_object = landmark_active.base_pose_camera - - -def xr_landmark_active_base_pose_location_update(self, context): - session_settings = context.window_manager.xr_session_settings - landmark_active = VRLandmark.get_active_landmark(context) - - session_settings.base_pose_location = landmark_active.base_pose_location - - -def xr_landmark_active_base_pose_angle_update(self, context): - session_settings = context.window_manager.xr_session_settings - landmark_active = VRLandmark.get_active_landmark(context) - - session_settings.base_pose_angle = landmark_active.base_pose_angle - - -def xr_landmark_type_update(self, context): - landmark_selected = VRLandmark.get_selected_landmark(context) - landmark_active = VRLandmark.get_active_landmark(context) - - # Only update session settings data if the changed landmark is actually - # the active one. - if landmark_active == landmark_selected: - xr_landmark_active_type_update(self, context) - - -def xr_landmark_camera_update(self, context): - landmark_selected = VRLandmark.get_selected_landmark(context) - landmark_active = VRLandmark.get_active_landmark(context) - - # Only update session settings data if the changed landmark is actually - # the active one. - if landmark_active == landmark_selected: - xr_landmark_active_camera_update(self, context) - - -def xr_landmark_base_pose_location_update(self, context): - landmark_selected = VRLandmark.get_selected_landmark(context) - landmark_active = VRLandmark.get_active_landmark(context) - - # Only update session settings data if the changed landmark is actually - # the active one. - if landmark_active == landmark_selected: - xr_landmark_active_base_pose_location_update(self, context) - - -def xr_landmark_base_pose_angle_update(self, context): - landmark_selected = VRLandmark.get_selected_landmark(context) - landmark_active = VRLandmark.get_active_landmark(context) - - # Only update session settings data if the changed landmark is actually - # the active one. - if landmark_active == landmark_selected: - xr_landmark_active_base_pose_angle_update(self, context) - - -def xr_landmark_camera_object_poll(self, object): - return object.type == 'CAMERA' - - -def xr_landmark_active_update(self, context): - wm = context.window_manager - - xr_landmark_active_type_update(self, context) - xr_landmark_active_camera_update(self, context) - xr_landmark_active_base_pose_location_update(self, context) - xr_landmark_active_base_pose_angle_update(self, context) - - if wm.xr_session_state: - wm.xr_session_state.reset_to_base_pose(context) - - -class VIEW3D_MT_landmark_menu(Menu): - bl_label = "Landmark Controls" - - def draw(self, _context): - layout = self.layout - - layout.operator("view3d.vr_landmark_from_camera") - layout.operator("view3d.update_vr_landmark") - layout.separator() - layout.operator("view3d.cursor_to_vr_landmark") - layout.operator("view3d.camera_to_vr_landmark") - layout.operator("view3d.add_camera_from_vr_landmark") - - -class VRLandmark(PropertyGroup): - name: bpy.props.StringProperty( - name="VR Landmark", - default="Landmark" - ) - type: bpy.props.EnumProperty( - name="Type", - items=[ - ('SCENE_CAMERA', "Scene Camera", - "Use scene's currently active camera to define the VR view base " - "location and rotation"), - ('USER_CAMERA', "Custom Camera", - "Use an existing camera to define the VR view base location and " - "rotation"), - ('CUSTOM', "Custom Pose", - "Allow a manually defined position and rotation to be used as " - "the VR view base pose"), - ], - default='SCENE_CAMERA', - update=xr_landmark_type_update, - ) - base_pose_camera: bpy.props.PointerProperty( - name="Camera", - type=bpy.types.Object, - poll=xr_landmark_camera_object_poll, - update=xr_landmark_camera_update, - ) - base_pose_location: bpy.props.FloatVectorProperty( - name="Base Pose Location", - subtype='TRANSLATION', - update=xr_landmark_base_pose_location_update, - ) - - base_pose_angle: bpy.props.FloatProperty( - name="Base Pose Angle", - subtype='ANGLE', - update=xr_landmark_base_pose_angle_update, - ) - - @staticmethod - def get_selected_landmark(context): - scene = context.scene - landmarks = scene.vr_landmarks - - return ( - None if (len(landmarks) < - 1) else landmarks[scene.vr_landmarks_selected] - ) - - @staticmethod - def get_active_landmark(context): - scene = context.scene - landmarks = scene.vr_landmarks - - return ( - None if (len(landmarks) < - 1) else landmarks[scene.vr_landmarks_active] - ) - - -class VIEW3D_UL_vr_landmarks(UIList): - def draw_item(self, context, layout, _data, item, icon, _active_data, - _active_propname, index): - landmark = item - landmark_active_idx = context.scene.vr_landmarks_active - - layout.emboss = 'NONE' - - layout.prop(landmark, "name", text="") - - icon = ( - 'RADIOBUT_ON' if (index == landmark_active_idx) else 'RADIOBUT_OFF' - ) - props = layout.operator( - "view3d.vr_landmark_activate", text="", icon=icon) - props.index = index - - -class VIEW3D_PT_vr_landmarks(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VR" - bl_label = "Landmarks" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - scene = context.scene - landmark_selected = VRLandmark.get_selected_landmark(context) - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - row = layout.row() - - row.template_list("VIEW3D_UL_vr_landmarks", "", scene, "vr_landmarks", - scene, "vr_landmarks_selected", rows=3) - - col = row.column(align=True) - col.operator("view3d.vr_landmark_add", icon='ADD', text="") - col.operator("view3d.vr_landmark_remove", icon='REMOVE', text="") - col.operator("view3d.vr_landmark_from_session", icon='PLUS', text="") - - col.menu("VIEW3D_MT_landmark_menu", icon='DOWNARROW_HLT', text="") - - if landmark_selected: - layout.prop(landmark_selected, "type") - - if landmark_selected.type == 'USER_CAMERA': - layout.prop(landmark_selected, "base_pose_camera") - elif landmark_selected.type == 'CUSTOM': - layout.prop(landmark_selected, - "base_pose_location", text="Location") - layout.prop(landmark_selected, - "base_pose_angle", text="Angle") - - -class VIEW3D_PT_vr_session_view(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VR" - bl_label = "View" - - def draw(self, context): - layout = self.layout - session_settings = context.window_manager.xr_session_settings - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - col = layout.column(align=True, heading="Show") - col.prop(session_settings, "show_floor", text="Floor") - col.prop(session_settings, "show_annotation", text="Annotations") - - col = layout.column(align=True) - col.prop(session_settings, "clip_start", text="Clip Start") - col.prop(session_settings, "clip_end", text="End") - - -class VIEW3D_PT_vr_session(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VR" - bl_label = "VR Session" - - def draw(self, context): - layout = self.layout - session_settings = context.window_manager.xr_session_settings - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - is_session_running = bpy.types.XrSessionState.is_running(context) - - # Using SNAP_FACE because it looks like a stop icon -- I shouldn't - # have commit rights... - toggle_info = ( - ("Start VR Session", 'PLAY') if not is_session_running else ( - "Stop VR Session", 'SNAP_FACE') - ) - layout.operator("wm.xr_session_toggle", - text=toggle_info[0], icon=toggle_info[1]) - - layout.separator() - - layout.prop(session_settings, "use_positional_tracking") - layout.prop(session_settings, "use_absolute_tracking") - - -class VIEW3D_PT_vr_info(bpy.types.Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VR" - bl_label = "VR Info" - - @classmethod - def poll(cls, context): - return not bpy.app.build_options.xr_openxr - - def draw(self, context): - layout = self.layout - layout.label(icon='ERROR', text="Built without VR/OpenXR features") - - -class VIEW3D_OT_vr_landmark_add(Operator): - bl_idname = "view3d.vr_landmark_add" - bl_label = "Add VR Landmark" - bl_description = "Add a new VR landmark to the list and select it" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - scene = context.scene - landmarks = scene.vr_landmarks - - landmarks.add() - - # select newly created set - scene.vr_landmarks_selected = len(landmarks) - 1 - - return {'FINISHED'} - - -class VIEW3D_OT_vr_landmark_from_camera(Operator): - bl_idname = "view3d.vr_landmark_from_camera" - bl_label = "Add VR Landmark from camera" - bl_description = "Add a new VR landmark from the active camera object to the list and select it" - bl_options = {'UNDO', 'REGISTER'} - - @classmethod - def poll(cls, context): - cam_selected = False - - vl_objects = bpy.context.view_layer.objects - if vl_objects.active and vl_objects.active.type == 'CAMERA': - cam_selected = True - return cam_selected - - def execute(self, context): - scene = context.scene - landmarks = scene.vr_landmarks - cam = context.view_layer.objects.active - lm = landmarks.add() - lm.type = 'USER_CAMERA' - lm.base_pose_camera = cam - lm.name = "LM_" + cam.name - - # select newly created set - scene.vr_landmarks_selected = len(landmarks) - 1 - - return {'FINISHED'} - - -class VIEW3D_OT_vr_landmark_from_session(Operator): - bl_idname = "view3d.vr_landmark_from_session" - bl_label = "Add VR Landmark from session" - bl_description = "Add VR landmark from the viewer pose of the running VR session to the list and select it" - bl_options = {'UNDO', 'REGISTER'} - - @classmethod - def poll(cls, context): - return bpy.types.XrSessionState.is_running(context) - - @staticmethod - def _calc_landmark_angle_from_viewer_rotation(rot): - from mathutils import Vector - - # We want an angle around Z based on the current viewer rotation. Idea - # is to create a vector from the viewer rotation, project that onto a - # Z-Up plane and use the resulting vector to get an angle around Z. - - view_rot_vec = Vector((0, 0, 1)) - view_rot_vec.rotate(rot) - angle_vec = view_rot_vec - view_rot_vec.project(Vector((0, 0, 1))) - - # We could probably use a 3D version of Vector.angle_signed() here, but - # that's not available. So manually calculate it via a quaternion delta. - forward_vec = Vector((0, -1, 0)) - diff = angle_vec.rotation_difference(forward_vec) - - return diff.angle * -diff.axis[2] - - def execute(self, context): - scene = context.scene - landmarks = scene.vr_landmarks - wm = context.window_manager - - lm = landmarks.add() - lm.type = 'CUSTOM' - scene.vr_landmarks_selected = len(landmarks) - 1 - - loc = wm.xr_session_state.viewer_pose_location - rot = wm.xr_session_state.viewer_pose_rotation - angle = self._calc_landmark_angle_from_viewer_rotation(rot) - - lm.base_pose_location = loc - lm.base_pose_angle = angle - - return {'FINISHED'} - - -class VIEW3D_OT_update_vr_landmark(Operator): - bl_idname = "view3d.update_vr_landmark" - bl_label = "Update Custom VR Landmark" - bl_description = "Update the selected landmark from the current viewer pose in the VR session" - bl_options = {'UNDO', 'REGISTER'} - - @classmethod - def poll(cls, context): - selected_landmark = VRLandmark.get_selected_landmark(context) - return bpy.types.XrSessionState.is_running(context) and selected_landmark.type == 'CUSTOM' - - def execute(self, context): - wm = context.window_manager - - lm = VRLandmark.get_selected_landmark(context) - - loc = wm.xr_session_state.viewer_pose_location - rot = wm.xr_session_state.viewer_pose_rotation.to_euler() - - lm.base_pose_location = loc - lm.base_pose_angle = rot - - # Re-activate the landmark to trigger viewer reset and flush landmark settings to the session settings. - xr_landmark_active_update(None, context) - - return {'FINISHED'} - - -class VIEW3D_OT_vr_landmark_remove(Operator): - bl_idname = "view3d.vr_landmark_remove" - bl_label = "Remove VR Landmark" - bl_description = "Delete the selected VR landmark from the list" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - scene = context.scene - landmarks = scene.vr_landmarks - - if len(landmarks) > 1: - landmark_selected_idx = scene.vr_landmarks_selected - landmarks.remove(landmark_selected_idx) - - scene.vr_landmarks_selected -= 1 - - return {'FINISHED'} - - -class VIEW3D_OT_cursor_to_vr_landmark(Operator): - bl_idname = "view3d.cursor_to_vr_landmark" - bl_label = "Cursor to VR Landmark" - bl_description = "Move the 3D Cursor to the selected VR Landmark" - bl_options = {'UNDO', 'REGISTER'} - - @classmethod - def poll(cls, context): - lm = VRLandmark.get_selected_landmark(context) - if lm.type == 'SCENE_CAMERA': - return context.scene.camera is not None - elif lm.type == 'USER_CAMERA': - return lm.base_pose_camera is not None - - return True - - def execute(self, context): - scene = context.scene - lm = VRLandmark.get_selected_landmark(context) - if lm.type == 'SCENE_CAMERA': - lm_pos = scene.camera.location - elif lm.type == 'USER_CAMERA': - lm_pos = lm.base_pose_camera.location - else: - lm_pos = lm.base_pose_location - scene.cursor.location = lm_pos - - return{'FINISHED'} - - -class VIEW3d_OT_add_camera_from_vr_landmark(Operator): - bl_idname = "view3d.add_camera_from_vr_landmark" - bl_label = "New Camera from VR Landmark" - bl_description = "Create a new Camera from the selected VR Landmark" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - import math - - scene = context.scene - lm = VRLandmark.get_selected_landmark(context) - - cam = bpy.data.cameras.new("Camera_" + lm.name) - new_cam = bpy.data.objects.new("Camera_" + lm.name, cam) - scene.collection.objects.link(new_cam) - angle = lm.base_pose_angle - new_cam.location = lm.base_pose_location - new_cam.rotation_euler = (math.pi, 0, angle) - - return {'FINISHED'} - - -class VIEW3D_OT_camera_to_vr_landmark(Operator): - bl_idname = "view3d.camera_to_vr_landmark" - bl_label = "Scene Camera to VR Landmark" - bl_description = "Position the scene camera at the selected landmark" - bl_options = {'UNDO', 'REGISTER'} - - @classmethod - def poll(cls, context): - return context.scene.camera is not None - - def execute(self, context): - import math - - scene = context.scene - lm = VRLandmark.get_selected_landmark(context) - - cam = scene.camera - angle = lm.base_pose_angle - cam.location = lm.base_pose_location - cam.rotation_euler = (math.pi / 2, 0, angle) - - return {'FINISHED'} - - -class VIEW3D_OT_vr_landmark_activate(Operator): - bl_idname = "view3d.vr_landmark_activate" - bl_label = "Activate VR Landmark" - bl_description = "Change to the selected VR landmark from the list" - bl_options = {'UNDO', 'REGISTER'} - - index: IntProperty( - name="Index", - options={'HIDDEN'}, - ) - - def execute(self, context): - scene = context.scene - - if self.index >= len(scene.vr_landmarks): - return {'CANCELLED'} - - scene.vr_landmarks_active = ( - self.index if self.properties.is_property_set( - "index") else scene.vr_landmarks_selected - ) - - return {'FINISHED'} - - -class VIEW3D_PT_vr_viewport_feedback(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VR" - bl_label = "Viewport Feedback" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - view3d = context.space_data - - col = layout.column(align=True) - col.label(icon='ERROR', text="Note:") - col.label(text="Settings here may have a significant") - col.label(text="performance impact!") - - layout.separator() - - layout.prop(view3d.shading, "vr_show_virtual_camera") - layout.prop(view3d.shading, "vr_show_landmarks") - layout.prop(view3d, "mirror_xr_session") - - -class VIEW3D_GT_vr_camera_cone(Gizmo): - bl_idname = "VIEW_3D_GT_vr_camera_cone" - - aspect = 1.0, 1.0 - - def draw(self, context): - import bgl - - if not hasattr(self, "frame_shape"): - aspect = self.aspect - - frame_shape_verts = ( - (-aspect[0], -aspect[1], -1.0), - (aspect[0], -aspect[1], -1.0), - (aspect[0], aspect[1], -1.0), - (-aspect[0], aspect[1], -1.0), - ) - lines_shape_verts = ( - (0.0, 0.0, 0.0), - frame_shape_verts[0], - (0.0, 0.0, 0.0), - frame_shape_verts[1], - (0.0, 0.0, 0.0), - frame_shape_verts[2], - (0.0, 0.0, 0.0), - frame_shape_verts[3], - ) - - self.frame_shape = self.new_custom_shape( - 'LINE_LOOP', frame_shape_verts) - self.lines_shape = self.new_custom_shape( - 'LINES', lines_shape_verts) - - # Ensure correct GL state (otherwise other gizmos might mess that up) - bgl.glLineWidth(1) - bgl.glEnable(bgl.GL_BLEND) - - self.draw_custom_shape(self.frame_shape) - self.draw_custom_shape(self.lines_shape) - - -class VIEW3D_GGT_vr_viewer_pose(GizmoGroup): - bl_idname = "VIEW3D_GGT_vr_viewer_pose" - bl_label = "VR Viewer Pose Indicator" - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'} - - @classmethod - def poll(cls, context): - view3d = context.space_data - return ( - view3d.shading.vr_show_virtual_camera and - bpy.types.XrSessionState.is_running(context) and - not view3d.mirror_xr_session - ) - - @staticmethod - def _get_viewer_pose_matrix(context): - from mathutils import Matrix, Quaternion - - wm = context.window_manager - - loc = wm.xr_session_state.viewer_pose_location - rot = wm.xr_session_state.viewer_pose_rotation - - rotmat = Matrix.Identity(3) - rotmat.rotate(rot) - rotmat.resize_4x4() - transmat = Matrix.Translation(loc) - - return transmat @ rotmat - - def setup(self, context): - gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname) - gizmo.aspect = 1 / 3, 1 / 4 - - gizmo.color = gizmo.color_highlight = 0.2, 0.6, 1.0 - gizmo.alpha = 1.0 - - self.gizmo = gizmo - - def draw_prepare(self, context): - self.gizmo.matrix_basis = self._get_viewer_pose_matrix(context) - - -class VIEW3D_GGT_vr_landmarks(GizmoGroup): - bl_idname = "VIEW3D_GGT_vr_landmarks" - bl_label = "VR Landmark Indicators" - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', 'SCALE'} - - @classmethod - def poll(cls, context): - view3d = context.space_data - return ( - view3d.shading.vr_show_landmarks - ) - - def setup(self, context): - pass - - def draw_prepare(self, context): - # first delete the old gizmos - for g in self.gizmos: - self.gizmos.remove(g) - - from math import radians - from mathutils import Matrix, Euler - scene = context.scene - landmarks = scene.vr_landmarks - - for lm in landmarks: - if ((lm.type == 'SCENE_CAMERA' and not scene.camera) or - (lm.type == 'USER_CAMERA' and not lm.base_pose_camera)): - continue - - gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname) - gizmo.aspect = 1 / 3, 1 / 4 - - gizmo.color = gizmo.color_highlight = 0.2, 1.0, 0.6 - gizmo.alpha = 1.0 - - self.gizmo = gizmo - - if lm.type == 'SCENE_CAMERA': - cam = scene.camera - lm_mat = cam.matrix_world if cam else Matrix.Identity(4) - elif lm.type == 'USER_CAMERA': - lm_mat = lm.base_pose_camera.matrix_world - else: - angle = lm.base_pose_angle - raw_rot = Euler((radians(90.0), 0, angle)) - - rotmat = Matrix.Identity(3) - rotmat.rotate(raw_rot) - rotmat.resize_4x4() - - transmat = Matrix.Translation(lm.base_pose_location) - - lm_mat = transmat @ rotmat - - self.gizmo.matrix_basis = lm_mat - - -classes = ( - VIEW3D_PT_vr_session, - VIEW3D_PT_vr_session_view, - VIEW3D_PT_vr_landmarks, - VIEW3D_PT_vr_viewport_feedback, - - VRLandmark, - VIEW3D_UL_vr_landmarks, - VIEW3D_MT_landmark_menu, - - VIEW3D_OT_vr_landmark_add, - VIEW3D_OT_vr_landmark_remove, - VIEW3D_OT_vr_landmark_activate, - VIEW3D_OT_vr_landmark_from_session, - VIEW3d_OT_add_camera_from_vr_landmark, - VIEW3D_OT_camera_to_vr_landmark, - VIEW3D_OT_vr_landmark_from_camera, - VIEW3D_OT_cursor_to_vr_landmark, - VIEW3D_OT_update_vr_landmark, - - VIEW3D_GT_vr_camera_cone, - VIEW3D_GGT_vr_viewer_pose, - VIEW3D_GGT_vr_landmarks, -) - - -def register(): - if not bpy.app.build_options.xr_openxr: - bpy.utils.register_class(VIEW3D_PT_vr_info) - return - - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Scene.vr_landmarks = CollectionProperty( - name="Landmark", - type=VRLandmark, - ) - bpy.types.Scene.vr_landmarks_selected = IntProperty( - name="Selected Landmark" - ) - bpy.types.Scene.vr_landmarks_active = IntProperty( - update=xr_landmark_active_update, - ) - # View3DShading is the only per 3D-View struct with custom property - # support, so "abusing" that to get a per 3D-View option. - bpy.types.View3DShading.vr_show_virtual_camera = BoolProperty( - name="Show VR Camera" - ) - bpy.types.View3DShading.vr_show_landmarks = BoolProperty( - name="Show Landmarks" - ) - - bpy.app.handlers.load_post.append(ensure_default_vr_landmark) - - -def unregister(): - if not bpy.app.build_options.xr_openxr: - bpy.utils.unregister_class(VIEW3D_PT_vr_info) - return - - for cls in classes: - bpy.utils.unregister_class(cls) - - del bpy.types.Scene.vr_landmarks - del bpy.types.Scene.vr_landmarks_selected - del bpy.types.Scene.vr_landmarks_active - del bpy.types.View3DShading.vr_show_virtual_camera - del bpy.types.View3DShading.vr_show_landmarks - - bpy.app.handlers.load_post.remove(ensure_default_vr_landmark) - - -if __name__ == "__main__": - register() diff --git a/viewport_vr_preview/__init__.py b/viewport_vr_preview/__init__.py new file mode 100644 index 00000000..352470ca --- /dev/null +++ b/viewport_vr_preview/__init__.py @@ -0,0 +1,68 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +bl_info = { + "name": "VR Scene Inspection", + "author": "Julian Eisel (Severin), Sebastian Koenig, Peter Kim (muxed-reality)", + "version": (0, 10, 0), + "blender": (3, 0, 0), + "location": "3D View > Sidebar > VR", + "description": ("View the viewport with virtual reality glasses " + "(head-mounted displays)"), + "support": "OFFICIAL", + "warning": "This is an early, limited preview of in development " + "VR support for Blender.", + "doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/vr_scene_inspection.html", + "category": "3D View", +} + + +if "bpy" in locals(): + import importlib + importlib.reload(action_map) + importlib.reload(gui) + importlib.reload(operators) + importlib.reload(properties) +else: + from . import action_map, gui, operators, properties + +import bpy + + +def register(): + if not bpy.app.build_options.xr_openxr: + bpy.utils.register_class(gui.VIEW3D_PT_vr_info) + return + + action_map.register() + gui.register() + operators.register() + properties.register() + + +def unregister(): + if not bpy.app.build_options.xr_openxr: + bpy.utils.unregister_class(gui.VIEW3D_PT_vr_info) + return + + action_map.unregister() + gui.unregister() + operators.unregister() + properties.unregister() diff --git a/viewport_vr_preview/action_map.py b/viewport_vr_preview/action_map.py new file mode 100644 index 00000000..49c3f7dd --- /dev/null +++ b/viewport_vr_preview/action_map.py @@ -0,0 +1,172 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +if "bpy" in locals(): + import importlib + importlib.reload(defaults) +else: + from . import action_map_io, defaults + +import bpy +from bpy.app.handlers import persistent +from bpy_extras.io_utils import ExportHelper, ImportHelper +import importlib.util +import os.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: + 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) + + +def vr_actions_use_gamepad_update(self, context): + vr_actionset_active_update(context) + + +@persistent +def vr_create_actions(context: bpy.context): + context = bpy.context + session_state = context.window_manager.xr_session_state + if not session_state: + return + + # Check if actions are enabled. + scene = context.scene + if not scene.vr_actions_enable: + return + + # Ensure default action maps. + if not defaults.vr_ensure_default_actionmaps(session_state): + return + + for am in session_state.actionmaps: + if len(am.actionmap_items) < 1: + continue + + ok = session_state.action_set_create(context, am) + if not ok: + return + + controller_grip_name = "" + controller_aim_name = "" + + for ami in am.actionmap_items: + if len(ami.bindings) < 1: + continue + + ok = session_state.action_create(context, am, ami) + if not ok: + return + + if ami.type == 'POSE': + if ami.pose_is_controller_grip: + controller_grip_name = ami.name + if ami.pose_is_controller_aim: + controller_aim_name = ami.name + + for amb in ami.bindings: + # Check for bindings that require OpenXR extensions. + if amb.name == defaults.VRDefaultActionbindings.REVERB_G2.value: + if not scene.vr_actions_enable_reverb_g2: + continue + elif amb.name == defaults.VRDefaultActionbindings.COSMOS.value: + if not scene.vr_actions_enable_cosmos: + continue + elif amb.name == defaults.VRDefaultActionbindings.HUAWEI.value: + if not scene.vr_actions_enable_huawei: + continue + + ok = session_state.action_binding_create(context, am, ami, amb) + if not ok: + return + + # Set controller pose actions. + if controller_grip_name and controller_aim_name: + session_state.controller_pose_actions_set(context, am.name, controller_grip_name, controller_aim_name) + + # Set active action set. + vr_actionset_active_update(context) + + +def vr_load_actionmaps(session_state, filepath): + if not os.path.exists(filepath): + return False + + spec = importlib.util.spec_from_file_location(os.path.basename(filepath), 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) + + return True + + +def vr_save_actionmaps(session_state, filepath, sort=False): + action_map_io.actionconfig_export_as_data(session_state, filepath, sort=sort) + + print("Saved XR actionmaps: " + filepath) + + return True + + +def register(): + bpy.types.Scene.vr_actions_enable = bpy.props.BoolProperty( + name="Use Controller Actions", + 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_cosmos = bpy.props.BoolProperty( + description="Enable bindings for the HTC Vive Cosmos controllers. Note that this may not be supported by all OpenXR runtimes", + default=False, + ) + 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, + ) + bpy.types.Scene.vr_actions_enable_reverb_g2 = bpy.props.BoolProperty( + description="Enable bindings for the HP Reverb G2 controllers. Note that this may not be supported by all OpenXR runtimes", + default=False, + ) + + bpy.app.handlers.xr_session_start_pre.append(vr_create_actions) + + +def unregister(): + del bpy.types.Scene.vr_actions_enable + del bpy.types.Scene.vr_actions_use_gamepad + 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 + + bpy.app.handlers.xr_session_start_pre.remove(vr_create_actions) diff --git a/viewport_vr_preview/action_map_io.py b/viewport_vr_preview/action_map_io.py new file mode 100644 index 00000000..c57543a0 --- /dev/null +++ b/viewport_vr_preview/action_map_io.py @@ -0,0 +1,348 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# ----------------------------------------------------------------------------- +# Export Functions + +__all__ = ( + "actionconfig_export_as_data", + "actionconfig_import_from_data", + "actionconfig_init_from_data", + "actionmap_init_from_data", + "actionmap_item_init_from_data", +) + + +def indent(levels): + return levels * " " + + +def round_float_32(f): + from struct import pack, unpack + return unpack("f", pack("f", f))[0] + + +def repr_f32(f): + f_round = round_float_32(f) + f_str = repr(f) + f_str_frac = f_str.partition(".")[2] + if not f_str_frac: + return f_str + for i in range(1, len(f_str_frac)): + f_test = round(f, i) + f_test_round = round_float_32(f_test) + if f_test_round == f_round: + return "%.*f" % (i, f_test) + return f_str + +def ami_args_as_data(ami): + s = [ + f"\"type\": '{ami.type}'", + f"\"user_path0\": '{ami.user_path0}'", + f"\"user_path1\": '{ami.user_path1}'", + ] + + if ami.type == 'FLOAT' or ami.type == 'VECTOR2D': + s.append(f"\"op\": '{ami.op}'") + s.append(f"\"op_mode\": '{ami.op_mode}'") + s.append(f"\"bimanual\": '{ami.bimanual}'") + s.append(f"\"haptic_name\": '{ami.haptic_name}'") + s.append(f"\"haptic_match_user_paths\": '{ami.haptic_match_user_paths}'") + s.append(f"\"haptic_duration\": '{ami.haptic_duration}'") + s.append(f"\"haptic_frequency\": '{ami.haptic_frequency}'") + s.append(f"\"haptic_amplitude\": '{ami.haptic_amplitude}'") + s.append(f"\"haptic_mode\": '{ami.haptic_mode}'") + elif ami.type == 'POSE': + s.append(f"\"pose_is_controller_grip\": '{ami.pose_is_controller_grip}'") + s.append(f"\"pose_is_controller_aim\": '{ami.pose_is_controller_aim}'") + + + return "{" + ", ".join(s) + "}" + + +def ami_data_from_args(ami, args): + ami.type = args["type"] + ami.user_path0 = args["user_path0"] + ami.user_path1 = args["user_path1"] + + if ami.type == 'FLOAT' or ami.type == 'VECTOR2D': + ami.op = args["op"] + ami.op_mode = args["op_mode"] + ami.bimanual = True if (args["bimanual"] == 'True') else False + ami.haptic_name = args["haptic_name"] + ami.haptic_match_user_paths = True if (args["haptic_match_user_paths"] == 'True') else False + ami.haptic_duration = float(args["haptic_duration"]) + ami.haptic_frequency = float(args["haptic_frequency"]) + ami.haptic_amplitude = float(args["haptic_amplitude"]) + ami.haptic_mode = args["haptic_mode"] + elif ami.type == 'POSE': + ami.pose_is_controller_grip = True if (args["pose_is_controller_grip"] == 'True') else False + ami.pose_is_controller_aim = True if (args["pose_is_controller_aim"] == 'True') else False + + +def _ami_properties_to_lines_recursive(level, properties, lines): + from bpy.types import OperatorProperties + + def string_value(value): + if isinstance(value, (str, bool, int, set)): + return repr(value) + elif isinstance(value, float): + return repr_f32(value) + elif getattr(value, '__len__', False): + return repr(tuple(value)) + raise Exception(f"Export action configuration: can't write {value!r}") + + for pname in properties.bl_rna.properties.keys(): + if pname != "rna_type": + value = getattr(properties, pname) + if isinstance(value, OperatorProperties): + lines_test = [] + _ami_properties_to_lines_recursive(level + 2, value, lines_test) + if lines_test: + lines.append(f"(") + lines.append(f"\"{pname}\",\n") + lines.append(f"{indent(level + 3)}" "[") + lines.extend(lines_test) + lines.append("],\n") + lines.append(f"{indent(level + 3)}" "),\n" f"{indent(level + 2)}") + del lines_test + elif properties.is_property_set(pname): + value = string_value(value) + lines.append((f"(\"{pname}\", {value:s}),\n" f"{indent(level + 2)}")) + + +def _ami_properties_to_lines(level, ami_props, lines): + if ami_props is None: + return + + lines_test = [f"\"op_properties\":\n" f"{indent(level + 1)}" "["] + _ami_properties_to_lines_recursive(level, ami_props, lines_test) + if len(lines_test) > 1: + lines_test.append("],\n") + lines.extend(lines_test) + + +def _ami_attrs_or_none(level, ami): + lines = [] + _ami_properties_to_lines(level + 1, ami.op_properties, lines) + if not lines: + return None + return "".join(lines) + + +def amb_args_as_data(amb, type): + s = [ + f"\"profile\": '{amb.profile}'", + f"\"component_path0\": '{amb.component_path0}'", + f"\"component_path1\": '{amb.component_path1}'", + ] + + if type == 'FLOAT' or type == 'VECTOR2D': + s.append(f"\"threshold\": '{amb.threshold}'") + if type == 'FLOAT': + s.append(f"\"axis_region\": '{amb.axis0_region}'") + else: # type == 'VECTOR2D': + s.append(f"\"axis0_region\": '{amb.axis0_region}'") + s.append(f"\"axis1_region\": '{amb.axis1_region}'") + elif type == 'POSE': + s.append(f"\"pose_location\": '{amb.pose_location.x, amb.pose_location.y, amb.pose_location.z}'") + s.append(f"\"pose_rotation\": '{amb.pose_rotation.x, amb.pose_rotation.y, amb.pose_rotation.z}'") + + return "{" + ", ".join(s) + "}" + + +def amb_data_from_args(amb, args, type): + amb.profile = args["profile"] + amb.component_path0 = args["component_path0"] + amb.component_path1 = args["component_path1"] + + if type == 'FLOAT' or type == 'VECTOR2D': + amb.threshold = float(args["threshold"]) + if type == 'FLOAT': + amb.axis0_region = args["axis_region"] + else: # type == 'VECTOR2D': + amb.axis0_region = args["axis0_region"] + amb.axis1_region = args["axis1_region"] + elif type == 'POSE': + l = args["pose_location"].strip(')(').split(', ') + amb.pose_location.x = float(l[0]) + amb.pose_location.y = float(l[1]) + amb.pose_location.z = float(l[2]) + l = args["pose_rotation"].strip(')(').split(', ') + amb.pose_rotation.x = float(l[0]) + amb.pose_rotation.y = float(l[1]) + amb.pose_rotation.z = float(l[2]) + + +def actionconfig_export_as_data(session_state, filepath, *, sort=False): + export_actionmaps = [] + + for am in session_state.actionmaps: + export_actionmaps.append(am) + + if sort: + export_actionmaps.sort(key=lambda k: k.name) + + with open(filepath, "w", encoding="utf-8") as fh: + fw = fh.write + + # Use the file version since it includes the sub-version + # which we can bump multiple times between releases. + from bpy.app import version_file + fw(f"actionconfig_version = {version_file!r}\n") + del version_file + + fw("actionconfig_data = \\\n[") + + for am in export_actionmaps: + fw("(") + fw(f"\"{am.name:s}\",\n") + + fw(f"{indent(2)}" "{") + fw(f"\"items\":\n") + fw(f"{indent(3)}[") + for ami in am.actionmap_items: + fw(f"(") + fw(f"\"{ami.name:s}\"") + ami_args = ami_args_as_data(ami) + ami_data = _ami_attrs_or_none(4, ami) + if ami_data is None: + fw(f", ") + else: + fw(",\n" f"{indent(5)}") + + fw(ami_args) + if ami_data is None: + fw(", None,\n") + else: + fw(",\n") + fw(f"{indent(5)}" "{") + fw(ami_data) + fw(f"{indent(6)}") + fw("}," f"{indent(5)}") + fw("\n") + + fw(f"{indent(5)}" "{") + fw(f"\"bindings\":\n") + fw(f"{indent(6)}[") + for amb in ami.bindings: + fw(f"(") + fw(f"\"{amb.name:s}\"") + fw(f", ") + amb_args = amb_args_as_data(amb, ami.type) + fw(amb_args) + fw("),\n" f"{indent(7)}") + fw("],\n" f"{indent(6)}") + fw("},\n" f"{indent(5)}") + fw("),\n" f"{indent(4)}") + + fw("],\n" f"{indent(3)}") + fw("},\n" f"{indent(2)}") + fw("),\n" f"{indent(1)}") + + fw("]\n") + fw("\n\n") + fw("if __name__ == \"__main__\":\n") + + # We could remove this in the future, as loading new action-maps in older Blender versions + # makes less and less sense as Blender changes. + fw(" # Only add keywords that are supported.\n") + fw(" from bpy.app import version as blender_version\n") + fw(" keywords = {}\n") + fw(" if blender_version >= (3, 0, 0):\n") + fw(" keywords[\"actionconfig_version\"] = actionconfig_version\n") + + fw(" import os\n") + fw(" from viewport_vr_preview.io import actionconfig_import_from_data\n") + fw(" actionconfig_import_from_data(\n") + fw(" os.path.splitext(os.path.basename(__file__))[0],\n") + fw(" actionconfig_data,\n") + fw(" **keywords,\n") + fw(" )\n") + + +# ----------------------------------------------------------------------------- +# Import Functions + +def _ami_props_setattr(ami_name, ami_props, attr, value): + if type(value) is list: + ami_subprop = getattr(ami_props, attr) + for subattr, subvalue in value: + _ami_props_setattr(ami_subprop, subattr, subvalue) + return + + try: + setattr(ami_props, attr, value) + except AttributeError: + print(f"Warning: property '{attr}' not found in action map item '{ami_name}'") + except Exception as ex: + print(f"Warning: {ex!r}") + + +def actionmap_item_init_from_data(ami, ami_bindings): + new_fn = getattr(ami.bindings, "new") + for (amb_name, amb_args) in ami_bindings: + amb = new_fn(amb_name, True) + amb_data_from_args(amb, amb_args, ami.type) + + +def actionmap_init_from_data(am, am_items): + new_fn = getattr(am.actionmap_items, "new") + for (ami_name, ami_args, ami_data, ami_content) in am_items: + ami = new_fn(ami_name, True) + ami_data_from_args(ami, ami_args) + if ami_data is not None: + ami_props_data = ami_data.get("op_properties", None) + if ami_props_data is not None: + ami_props = ami.op_properties + assert type(ami_props_data) is list + for attr, value in ami_props_data: + _ami_props_setattr(ami_name, ami_props, attr, value) + ami_bindings = ami_content["bindings"] + assert type(ami_bindings) is list + actionmap_item_init_from_data(ami, ami_bindings) + + +def actionconfig_init_from_data(session_state, actionconfig_data, actionconfig_version): + # Load data in the format defined above. + # + # Runs at load time, keep this fast! + if actionconfig_version is not None: + from .versioning import actionconfig_update + 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_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. + # + # For full action maps, ensure these are always lists to allow for extending them + # in a generic way that doesn't have to check for the type each time. + assert type(am_items) is list + actionmap_init_from_data(am, am_items) + + +def actionconfig_import_from_data(session_state, 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) diff --git a/viewport_vr_preview/configs/default.py b/viewport_vr_preview/configs/default.py new file mode 100644 index 00000000..efb130c1 --- /dev/null +++ b/viewport_vr_preview/configs/default.py @@ -0,0 +1,406 @@ +actionconfig_version = (3, 0, 39) +actionconfig_data = \ +[("blender_default", + {"items": + [("controller_grip", {"type": 'POSE', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right', "pose_is_controller_grip": 'True', "pose_is_controller_aim": 'False'}, None, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("simple", {"profile": '/interaction_profiles/khr/simple_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/grip/pose', "component_path1": '/input/grip/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ], + }, + ), + ("controller_aim", {"type": 'POSE', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right', "pose_is_controller_grip": 'False', "pose_is_controller_aim": 'True'}, None, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("simple", {"profile": '/interaction_profiles/khr/simple_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/aim/pose', "component_path1": '/input/aim/pose', "pose_location": '(0.0, 0.0, 0.0)', "pose_rotation": '(0.0, 0.0, 0.0)'}), + ], + }, + ), + ("teleport", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right', "op": 'wm.xr_navigation_teleport', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("interpolation", 0.9), + ("color", (0.0, 1.0, 1.0, 1.0)), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("simple", {"profile": '/interaction_profiles/khr/simple_controller', "component_path0": '/input/select/click', "component_path1": '/input/select/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/trigger/value', "component_path1": '/input/trigger/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("nav_grab", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right', "op": 'wm.xr_navigation_grab', "op_mode": 'MODAL', "bimanual": 'True', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("lock_rotation", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/squeeze/click', "component_path1": '/input/squeeze/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/click', "component_path1": '/input/trackpad/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/squeeze/value', "component_path1": '/input/squeeze/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/squeeze/value', "component_path1": '/input/squeeze/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/squeeze/value', "component_path1": '/input/squeeze/value', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("simple", {"profile": '/interaction_profiles/khr/simple_controller', "component_path0": '/input/menu/click', "component_path1": '/input/menu/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/squeeze/click', "component_path1": '/input/squeeze/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/squeeze/click', "component_path1": '/input/squeeze/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("fly_forward", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_FORWARD'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_back", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_BACK'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_left", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_LEFT'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_right", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_RIGHT'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_up", {"type": 'FLOAT', "user_path0": '/user/hand/right', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'UP'), + ("speed_min", 0.014), + ("speed_max", 0.042), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_down", {"type": 'FLOAT', "user_path0": '/user/hand/right', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'DOWN'), + ("speed_min", 0.014), + ("speed_max", 0.042), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_turnleft", {"type": 'FLOAT', "user_path0": '/user/hand/right', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'TURNLEFT'), + ("speed_min", 0.01), + ("speed_max", 0.03), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_turnright", {"type": 'FLOAT', "user_path0": '/user/hand/right', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'TURNRIGHT'), + ("speed_min", 0.01), + ("speed_max", 0.03), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/trackpad/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/thumbstick/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("nav_reset", {"type": 'FLOAT', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right', "op": 'wm.xr_navigation_reset', "op_mode": 'PRESS', "bimanual": 'False', "haptic_name": 'haptic', "haptic_match_user_paths": 'True', "haptic_duration": '0.30000001192092896', "haptic_frequency": '3000.0', "haptic_amplitude": '0.5', "haptic_mode": 'PRESS'}, + {"op_properties": + [("location", False), + ("rotation", False), + ("scale", True), + ], + }, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/input/x/click', "component_path1": '/input/a/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/input/back/click', "component_path1": '/input/back/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/input/a/click', "component_path1": '/input/a/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/input/x/click', "component_path1": '/input/a/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/input/x/click', "component_path1": '/input/a/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/input/menu/click', "component_path1": '/input/menu/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/input/menu/click', "component_path1": '/input/menu/click', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("haptic", {"type": 'VIBRATION', "user_path0": '/user/hand/left', "user_path1": '/user/hand/right'}, None, + {"bindings": + [("cosmos", {"profile": '/interaction_profiles/htc/vive_cosmos_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("huawei", {"profile": '/interaction_profiles/huawei/controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("index", {"profile": '/interaction_profiles/valve/index_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("oculus", {"profile": '/interaction_profiles/oculus/touch_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("reverb_g2", {"profile": '/interaction_profiles/hp/mixed_reality_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("simple", {"profile": '/interaction_profiles/khr/simple_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("vive", {"profile": '/interaction_profiles/htc/vive_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ("wmr", {"profile": '/interaction_profiles/microsoft/motion_controller', "component_path0": '/output/haptic', "component_path1": '/output/haptic'}), + ], + }, + ), + ], + }, + ), + ("blender_default_gamepad", + {"items": + [("teleport", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_teleport', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("interpolation", 0.9), + ("from_viewer", True), + ("color", (0.0, 1.0, 1.0, 1.0)), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/trigger_right/value', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("fly", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, None, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/trigger_left/value', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("fly_forward", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_FORWARD'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_left/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_back", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_BACK'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_left/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_left", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_LEFT'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_left/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_right", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'VIEWER_RIGHT'), + ("lock_location_z", True), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_left/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_up", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'UP'), + ("speed_min", 0.014), + ("speed_max", 0.042), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_right/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("fly_down", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'DOWN'), + ("speed_min", 0.014), + ("speed_max", 0.042), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_right/y', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_turnleft", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'TURNLEFT'), + ("speed_min", 0.01), + ("speed_max", 0.03), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_right/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'NEGATIVE'}), + ], + }, + ), + ("fly_turnright", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_fly', "op_mode": 'MODAL', "bimanual": 'False', "haptic_name": '', "haptic_match_user_paths": 'False', "haptic_duration": '0.0', "haptic_frequency": '0.0', "haptic_amplitude": '0.0', "haptic_mode": 'PRESS'}, + {"op_properties": + [("mode", 'TURNRIGHT'), + ("speed_min", 0.01), + ("speed_max", 0.03), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/thumbstick_right/x', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'POSITIVE'}), + ], + }, + ), + ("nav_reset", {"type": 'FLOAT', "user_path0": '/user/gamepad', "user_path1": '', "op": 'wm.xr_navigation_reset', "op_mode": 'PRESS', "bimanual": 'False', "haptic_name": 'haptic_right', "haptic_match_user_paths": 'True', "haptic_duration": '0.30000001192092896', "haptic_frequency": '3000.0', "haptic_amplitude": '0.5', "haptic_mode": 'PRESS'}, + {"op_properties": + [("location", False), + ("rotation", False), + ("scale", True), + ], + }, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/input/a/click', "component_path1": '', "threshold": '0.30000001192092896', "axis_region": 'ANY'}), + ], + }, + ), + ("haptic_left", {"type": 'VIBRATION', "user_path0": '/user/gamepad', "user_path1": ''}, None, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/output/haptic_left', "component_path1": ''}), + ], + }, + ), + ("haptic_right", {"type": 'VIBRATION', "user_path0": '/user/gamepad', "user_path1": ''}, None, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/output/haptic_right', "component_path1": ''}), + ], + }, + ), + ("haptic_lefttrigger", {"type": 'VIBRATION', "user_path0": '/user/gamepad', "user_path1": ''}, None, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/output/haptic_left_trigger', "component_path1": ''}), + ], + }, + ), + ("haptic_righttrigger", {"type": 'VIBRATION', "user_path0": '/user/gamepad', "user_path1": ''}, None, + {"bindings": + [("gamepad", {"profile": '/interaction_profiles/microsoft/xbox_controller', "component_path0": '/output/haptic_right_trigger', "component_path1": ''}), + ], + }, + ), + ], + }, + ), + ] + + +if __name__ == "__main__": + # Only add keywords that are supported. + from bpy.app import version as blender_version + keywords = {} + if blender_version >= (3, 0, 0): + keywords["actionconfig_version"] = actionconfig_version + import os + from viewport_vr_preview.io import actionconfig_import_from_data + actionconfig_import_from_data( + os.path.splitext(os.path.basename(__file__))[0], + actionconfig_data, + **keywords, + ) diff --git a/viewport_vr_preview/defaults.py b/viewport_vr_preview/defaults.py new file mode 100644 index 00000000..205e39d2 --- /dev/null +++ b/viewport_vr_preview/defaults.py @@ -0,0 +1,1518 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +if "bpy" in locals(): + import importlib + importlib.reload(action_map) +else: + from . import action_map + +import bpy +from bpy.app.handlers import persistent +from enum import Enum +import math +import os.path + + +# Default action maps. +class VRDefaultActionmaps(Enum): + DEFAULT = "blender_default" + GAMEPAD = "blender_default_gamepad" + + +# Default actions. +class VRDefaultActions(Enum): + CONTROLLER_GRIP = "controller_grip" + CONTROLLER_AIM = "controller_aim" + TELEPORT = "teleport" + NAV_GRAB = "nav_grab" + FLY = "fly" + FLY_FORWARD = "fly_forward" + FLY_BACK = "fly_back" + FLY_LEFT = "fly_left" + FLY_RIGHT = "fly_right" + FLY_UP = "fly_up" + FLY_DOWN = "fly_down" + FLY_TURNLEFT = "fly_turnleft" + FLY_TURNRIGHT = "fly_turnright" + NAV_RESET = "nav_reset" + HAPTIC = "haptic" + HAPTIC_LEFT = "haptic_left" + HAPTIC_RIGHT = "haptic_right" + HAPTIC_LEFTTRIGGER = "haptic_lefttrigger" + HAPTIC_RIGHTTRIGGER = "haptic_righttrigger" + + +# Default action bindings. +class VRDefaultActionbindings(Enum): + COSMOS = "cosmos" + GAMEPAD = "gamepad" + HUAWEI = "huawei" + INDEX = "index" + OCULUS = "oculus" + REVERB_G2 = "reverb_g2" + SIMPLE = "simple" + VIVE = "vive" + WMR = "wmr" + + +class VRDefaultActionprofiles(Enum): + COSMOS = "/interaction_profiles/htc/vive_cosmos_controller" + GAMEPAD = "/interaction_profiles/microsoft/xbox_controller" + HUAWEI = "/interaction_profiles/huawei/controller" + INDEX = "/interaction_profiles/valve/index_controller" + OCULUS = "/interaction_profiles/oculus/touch_controller" + REVERB_G2 = "/interaction_profiles/hp/mixed_reality_controller" + SIMPLE = "/interaction_profiles/khr/simple_controller" + VIVE = "/interaction_profiles/htc/vive_controller" + WMR = "/interaction_profiles/microsoft/motion_controller" + + +def vr_defaults_actionmap_add(session_state, name): + am = session_state.actionmaps.new(session_state, name, True) + + return am + + +def vr_defaults_action_add(am, + name, + user_path0, + user_path1, + op, + op_mode, + bimanual, + haptic_name, + haptic_match_user_paths, + haptic_duration, + haptic_frequency, + haptic_amplitude, + haptic_mode): + + + ami = am.actionmap_items.new(name, True) + if ami: + ami.type = 'FLOAT' + ami.user_path0 = user_path0 + ami.user_path1 = user_path1 + ami.op = op + ami.op_mode = op_mode + ami.bimanual = bimanual + ami.haptic_name = haptic_name + ami.haptic_match_user_paths = haptic_match_user_paths + ami.haptic_duration = haptic_duration + ami.haptic_frequency = haptic_frequency + ami.haptic_amplitude = haptic_amplitude + ami.haptic_mode = haptic_mode + + return ami + + +def vr_defaults_pose_action_add(am, + name, + user_path0, + user_path1, + is_controller_grip, + is_controller_aim): + ami = am.actionmap_items.new(name, True) + if ami: + ami.type = 'POSE' + ami.user_path0 = user_path0 + ami.user_path1 = user_path1 + ami.pose_is_controller_grip = is_controller_grip + ami.pose_is_controller_aim = is_controller_aim + + return ami + + +def vr_defaults_haptic_action_add(am, + name, + user_path0, + user_path1): + ami = am.actionmap_items.new(name, True) + if ami: + ami.type = 'VIBRATION' + ami.user_path0 = user_path0 + ami.user_path1 = user_path1 + + return ami + + +def vr_defaults_actionbinding_add(ami, + name, + profile, + component_path0, + component_path1, + threshold, + axis0_region, + axis1_region): + amb = ami.bindings.new(name, True) + if amb: + amb.profile = profile + amb.component_path0 = component_path0 + amb.component_path1 = component_path1 + amb.threshold = threshold + amb.axis0_region = axis0_region + amb.axis1_region = axis1_region + + return amb + + +def vr_defaults_pose_actionbinding_add(ami, + name, + profile, + component_path0, + component_path1, + location, + rotation): + amb = ami.bindings.new(name, True) + if amb: + amb.profile = profile + amb.component_path0 = component_path0 + amb.component_path1 = component_path1 + amb.pose_location = location + amb.pose_rotation = rotation + + return amb + + +def vr_defaults_haptic_actionbinding_add(ami, + name, + profile, + component_path0, + component_path1): + amb = ami.bindings.new(name, True) + if amb: + amb.profile = profile + amb.component_path0 = component_path0 + amb.component_path1 = component_path1 + + + return amb + + +def vr_defaults_create_default(session_state): + am = vr_defaults_actionmap_add(session_state, + VRDefaultActionmaps.DEFAULT.value) + if not am: + return + + ami = vr_defaults_pose_action_add(am, + VRDefaultActions.CONTROLLER_GRIP.value, + "/user/hand/left", + "/user/hand/right", + True, + False) + if ami: + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.SIMPLE.value, + VRDefaultActionprofiles.SIMPLE.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/grip/pose", + "/input/grip/pose", + (0, 0, 0), + (0, 0, 0)) + + ami = vr_defaults_pose_action_add(am, + VRDefaultActions.CONTROLLER_AIM.value, + "/user/hand/left", + "/user/hand/right", + False, + True) + if ami: + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.SIMPLE.value, + VRDefaultActionprofiles.SIMPLE.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + vr_defaults_pose_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/aim/pose", + "/input/aim/pose", + (0, 0, 0), + (0, 0, 0)) + + ami = vr_defaults_action_add(am, + VRDefaultActions.TELEPORT.value, + "/user/hand/left", + "/user/hand/right", + "wm.xr_navigation_teleport", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.SIMPLE.value, + VRDefaultActionprofiles.SIMPLE.value, + "/input/select/click", + "/input/select/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/trigger/value", + "/input/trigger/value", + 0.3, + 'ANY', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.NAV_GRAB.value, + "/user/hand/left", + "/user/hand/right", + "wm.xr_navigation_grab", + 'MODAL', + True, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/squeeze/click", + "/input/squeeze/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/click", + "/input/trackpad/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/squeeze/value", + "/input/squeeze/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/squeeze/value", + "/input/squeeze/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/squeeze/value", + "/input/squeeze/value", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.SIMPLE.value, + VRDefaultActionprofiles.SIMPLE.value, + "/input/menu/click", + "/input/menu/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/squeeze/click", + "/input/squeeze/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/squeeze/click", + "/input/squeeze/click", + 0.3, + 'ANY', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_FORWARD.value, + "/user/hand/left", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_BACK.value, + "/user/hand/left", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_LEFT.value, + "/user/hand/left", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_RIGHT.value, + "/user/hand/left", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_UP.value, + "/user/hand/right", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_DOWN.value, + "/user/hand/right", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_TURNLEFT.value, + "/user/hand/right", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_TURNRIGHT.value, + "/user/hand/right", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/trackpad/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/trackpad/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/thumbstick/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.NAV_RESET.value, + "/user/hand/left", + "/user/hand/right", + "wm.xr_navigation_reset", + 'PRESS', + False, + "haptic", + True, + 0.3, + 3000.0, + 0.5, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/input/x/click", + "/input/a/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/input/back/click", + "/input/back/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/input/a/click", + "/input/a/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/input/x/click", + "/input/a/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/input/x/click", + "/input/a/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/input/menu/click", + "/input/menu/click", + 0.3, + 'ANY', + 'ANY') + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/input/menu/click", + "/input/menu/click", + 0.3, + 'ANY', + 'ANY') + + ami = vr_defaults_haptic_action_add(am, + VRDefaultActions.HAPTIC.value, + "/user/hand/left", + "/user/hand/right") + if ami: + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.COSMOS.value, + VRDefaultActionprofiles.COSMOS.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.HUAWEI.value, + VRDefaultActionprofiles.HUAWEI.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.INDEX.value, + VRDefaultActionprofiles.INDEX.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.OCULUS.value, + VRDefaultActionprofiles.OCULUS.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.REVERB_G2.value, + VRDefaultActionprofiles.REVERB_G2.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.SIMPLE.value, + VRDefaultActionprofiles.SIMPLE.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.VIVE.value, + VRDefaultActionprofiles.VIVE.value, + "/output/haptic", + "/output/haptic") + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.WMR.value, + VRDefaultActionprofiles.WMR.value, + "/output/haptic", + "/output/haptic") + + +def vr_defaults_create_default_gamepad(session_state): + am = vr_defaults_actionmap_add(session_state, + VRDefaultActionmaps.GAMEPAD.value) + + ami = vr_defaults_action_add(am, + VRDefaultActions.TELEPORT.value, + "/user/gamepad", + "", + "wm.xr_navigation_teleport", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/trigger_right/value", + "", + 0.3, + 'ANY', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/trigger_left/value", + "", + 0.3, + 'ANY', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_FORWARD.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_left/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_BACK.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_left/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_LEFT.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_left/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_RIGHT.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_left/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_UP.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_right/y", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_DOWN.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_right/y", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_TURNLEFT.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_right/x", + "", + 0.3, + 'NEGATIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.FLY_TURNRIGHT.value, + "/user/gamepad", + "", + "wm.xr_navigation_fly", + 'MODAL', + False, + "", + False, + 0.0, + 0.0, + 0.0, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/thumbstick_right/x", + "", + 0.3, + 'POSITIVE', + 'ANY') + + ami = vr_defaults_action_add(am, + VRDefaultActions.NAV_RESET.value, + "/user/gamepad", + "", + "wm.xr_navigation_reset", + 'PRESS', + False, + "haptic_right", + True, + 0.3, + 3000.0, + 0.5, + 'PRESS') + if ami: + vr_defaults_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/input/a/click", + "", + 0.3, + 'ANY', + 'ANY') + + ami =vr_defaults_haptic_action_add(am, + VRDefaultActions.HAPTIC_LEFT.value, + "/user/gamepad", + "") + if ami: + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/output/haptic_left", + "") + + ami =vr_defaults_haptic_action_add(am, + VRDefaultActions.HAPTIC_RIGHT.value, + "/user/gamepad", + "") + if ami: + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/output/haptic_right", + "") + + ami = vr_defaults_haptic_action_add(am, + VRDefaultActions.HAPTIC_LEFTTRIGGER.value, + "/user/gamepad", + "") + if ami: + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/output/haptic_left_trigger", + "") + + ami = vr_defaults_haptic_action_add(am, + VRDefaultActions.HAPTIC_RIGHTTRIGGER.value, + "/user/gamepad", + "") + if ami: + vr_defaults_haptic_actionbinding_add(ami, + VRDefaultActionbindings.GAMEPAD.value, + VRDefaultActionprofiles.GAMEPAD.value, + "/output/haptic_right_trigger", + "") + + +def vr_get_default_config_path(): + filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "configs") + return os.path.join(filepath, "default.py") + + +def vr_ensure_default_actionmaps(session_state): + loaded = True + + for name in VRDefaultActionmaps: + if not session_state.actionmaps.find(session_state, name.value): + loaded = False + break + + if loaded: + return loaded + + # Load default action maps. + filepath = vr_get_default_config_path() + + 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) + + action_map.vr_save_actionmaps(session_state, filepath, sort=False) + + loaded = action_map.vr_load_actionmaps(session_state, filepath) + + return loaded diff --git a/viewport_vr_preview/gui.py b/viewport_vr_preview/gui.py new file mode 100644 index 00000000..7778882d --- /dev/null +++ b/viewport_vr_preview/gui.py @@ -0,0 +1,277 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +if "bpy" in locals(): + import importlib + importlib.reload(properties) +else: + from . import properties + +import bpy +from bpy.types import ( + Menu, + Panel, + UIList, +) + +### Session. +class VIEW3D_PT_vr_session(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "VR Session" + + def draw(self, context): + layout = self.layout + session_settings = context.window_manager.xr_session_settings + scene = context.scene + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + is_session_running = bpy.types.XrSessionState.is_running(context) + + # Using SNAP_FACE because it looks like a stop icon -- I shouldn't + # have commit rights... + toggle_info = ( + ("Start VR Session", 'PLAY') if not is_session_running else ( + "Stop VR Session", 'SNAP_FACE') + ) + layout.operator("wm.xr_session_toggle", + text=toggle_info[0], icon=toggle_info[1]) + + layout.separator() + + col = layout.column(align=True, heading="Tracking") + col.prop(session_settings, "use_positional_tracking", text="Positional") + col.prop(session_settings, "use_absolute_tracking", text="Absolute") + + col = layout.column(align=True, heading="Actions") + col.prop(scene, "vr_actions_enable") + + +### View. +class VIEW3D_PT_vr_session_view(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "View" + + def draw(self, context): + layout = self.layout + session_settings = context.window_manager.xr_session_settings + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + 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") + + col = layout.column(align=True) + col.prop(session_settings, "controller_draw_style", text="Controller Style") + + col = layout.column(align=True) + col.prop(session_settings, "clip_start", text="Clip Start") + col.prop(session_settings, "clip_end", text="End") + + +### Landmarks. +class VIEW3D_MT_vr_landmark_menu(Menu): + bl_label = "Landmark Controls" + + def draw(self, _context): + layout = self.layout + + layout.operator("view3d.vr_landmark_from_camera") + layout.operator("view3d.update_vr_landmark") + layout.separator() + layout.operator("view3d.cursor_to_vr_landmark") + layout.operator("view3d.camera_to_vr_landmark") + layout.operator("view3d.add_camera_from_vr_landmark") + + +class VIEW3D_UL_vr_landmarks(UIList): + def draw_item(self, context, layout, _data, item, icon, _active_data, + _active_propname, index): + landmark = item + landmark_active_idx = context.scene.vr_landmarks_active + + layout.emboss = 'NONE' + + layout.prop(landmark, "name", text="") + + icon = ( + 'RADIOBUT_ON' if (index == landmark_active_idx) else 'RADIOBUT_OFF' + ) + props = layout.operator( + "view3d.vr_landmark_activate", text="", icon=icon) + props.index = index + + +class VIEW3D_PT_vr_landmarks(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "Landmarks" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + scene = context.scene + landmark_selected = properties.VRLandmark.get_selected_landmark(context) + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + row = layout.row() + + row.template_list("VIEW3D_UL_vr_landmarks", "", scene, "vr_landmarks", + scene, "vr_landmarks_selected", rows=3) + + col = row.column(align=True) + col.operator("view3d.vr_landmark_add", icon='ADD', text="") + col.operator("view3d.vr_landmark_remove", icon='REMOVE', text="") + col.operator("view3d.vr_landmark_from_session", icon='PLUS', text="") + + col.menu("VIEW3D_MT_vr_landmark_menu", icon='DOWNARROW_HLT', text="") + + if landmark_selected: + layout.prop(landmark_selected, "type") + + if landmark_selected.type == 'OBJECT': + layout.prop(landmark_selected, "base_pose_object") + layout.prop(landmark_selected, "base_scale", text="Scale") + elif landmark_selected.type == 'CUSTOM': + layout.prop(landmark_selected, + "base_pose_location", text="Location") + layout.prop(landmark_selected, + "base_pose_angle", text="Angle") + layout.prop(landmark_selected, + "base_scale", text="Scale") + + +### View. +class VIEW3D_PT_vr_actionmaps(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "Action Maps" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + scene = context.scene + + 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_cosmos", text="HTC Vive Cosmos") + col.prop(scene, "vr_actions_enable_huawei", text="Huawei") + + +### Viewport feedback. +class VIEW3D_PT_vr_viewport_feedback(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "Viewport Feedback" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + scene = context.scene + view3d = context.space_data + session_settings = context.window_manager.xr_session_settings + + col = layout.column(align=True) + col.label(icon='ERROR', text="Note:") + col.label(text="Settings here may have a significant") + col.label(text="performance impact!") + + layout.separator() + + layout.prop(view3d.shading, "vr_show_virtual_camera") + layout.prop(view3d.shading, "vr_show_controllers") + layout.prop(view3d.shading, "vr_show_landmarks") + layout.prop(view3d, "mirror_xr_session") + + +### Info. +class VIEW3D_PT_vr_info(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "VR" + bl_label = "VR Info" + + @classmethod + def poll(cls, context): + return not bpy.app.build_options.xr_openxr + + def draw(self, context): + layout = self.layout + layout.label(icon='ERROR', text="Built without VR/OpenXR features.") + + +classes = ( + VIEW3D_PT_vr_session, + VIEW3D_PT_vr_session_view, + VIEW3D_PT_vr_landmarks, + VIEW3D_PT_vr_actionmaps, + VIEW3D_PT_vr_viewport_feedback, + + VIEW3D_UL_vr_landmarks, + VIEW3D_MT_vr_landmark_menu, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + # View3DShading is the only per 3D-View struct with custom property + # support, so "abusing" that to get a per 3D-View option. + bpy.types.View3DShading.vr_show_virtual_camera = bpy.props.BoolProperty( + name="Show VR Camera" + ) + bpy.types.View3DShading.vr_show_controllers = bpy.props.BoolProperty( + name="Show VR Controllers" + ) + bpy.types.View3DShading.vr_show_landmarks = bpy.props.BoolProperty( + name="Show Landmarks" + ) + + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + + 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/operators.py b/viewport_vr_preview/operators.py new file mode 100644 index 00000000..932dac31 --- /dev/null +++ b/viewport_vr_preview/operators.py @@ -0,0 +1,521 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +if "bpy" in locals(): + import importlib + importlib.reload(properties) +else: + from . import properties + +import bpy +from bpy.types import ( + Gizmo, + GizmoGroup, + Operator, +) +import bgl +import math +from math import radians +from mathutils import Euler, Matrix, Quaternion, Vector + + +### Landmarks. +class VIEW3D_OT_vr_landmark_add(Operator): + bl_idname = "view3d.vr_landmark_add" + bl_label = "Add VR Landmark" + bl_description = "Add a new VR landmark to the list and select it" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + scene = context.scene + landmarks = scene.vr_landmarks + + landmarks.add() + + # select newly created set + scene.vr_landmarks_selected = len(landmarks) - 1 + + return {'FINISHED'} + + +class VIEW3D_OT_vr_landmark_from_camera(Operator): + bl_idname = "view3d.vr_landmark_from_camera" + bl_label = "Add VR Landmark from Camera" + bl_description = "Add a new VR landmark from the active camera object to the list and select it" + bl_options = {'UNDO', 'REGISTER'} + + @classmethod + def poll(cls, context): + cam_selected = False + + vl_objects = bpy.context.view_layer.objects + if vl_objects.active and vl_objects.active.type == 'CAMERA': + cam_selected = True + return cam_selected + + def execute(self, context): + scene = context.scene + landmarks = scene.vr_landmarks + cam = context.view_layer.objects.active + lm = landmarks.add() + lm.type = 'OBJECT' + lm.base_pose_object = cam + lm.name = "LM_" + cam.name + + # select newly created set + scene.vr_landmarks_selected = len(landmarks) - 1 + + return {'FINISHED'} + + +class VIEW3D_OT_vr_landmark_from_session(Operator): + bl_idname = "view3d.vr_landmark_from_session" + bl_label = "Add VR Landmark from Session" + bl_description = "Add VR landmark from the viewer pose of the running VR session to the list and select it" + bl_options = {'UNDO', 'REGISTER'} + + @classmethod + def poll(cls, context): + return bpy.types.XrSessionState.is_running(context) + + def execute(self, context): + scene = context.scene + landmarks = scene.vr_landmarks + wm = context.window_manager + + lm = landmarks.add() + lm.type = "CUSTOM" + scene.vr_landmarks_selected = len(landmarks) - 1 + + loc = wm.xr_session_state.viewer_pose_location + rot = wm.xr_session_state.viewer_pose_rotation.to_euler() + + lm.base_pose_location = loc + lm.base_pose_angle = rot[2] + + return {'FINISHED'} + + +class VIEW3D_OT_update_vr_landmark(Operator): + bl_idname = "view3d.update_vr_landmark" + bl_label = "Update Custom VR Landmark" + bl_description = "Update the selected landmark from the current viewer pose in the VR session" + bl_options = {'UNDO', 'REGISTER'} + + @classmethod + def poll(cls, context): + selected_landmark = properties.VRLandmark.get_selected_landmark(context) + return bpy.types.XrSessionState.is_running(context) and selected_landmark.type == 'CUSTOM' + + def execute(self, context): + wm = context.window_manager + + lm = properties.VRLandmark.get_selected_landmark(context) + + loc = wm.xr_session_state.viewer_pose_location + rot = wm.xr_session_state.viewer_pose_rotation.to_euler() + + lm.base_pose_location = loc + lm.base_pose_angle = rot + + # Re-activate the landmark to trigger viewer reset and flush landmark settings to the session settings. + properties.vr_landmark_active_update(None, context) + + return {'FINISHED'} + + +class VIEW3D_OT_vr_landmark_remove(Operator): + bl_idname = "view3d.vr_landmark_remove" + bl_label = "Remove VR Landmark" + bl_description = "Delete the selected VR landmark from the list" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + scene = context.scene + landmarks = scene.vr_landmarks + + if len(landmarks) > 1: + landmark_selected_idx = scene.vr_landmarks_selected + landmarks.remove(landmark_selected_idx) + + scene.vr_landmarks_selected -= 1 + + return {'FINISHED'} + + +class VIEW3D_OT_cursor_to_vr_landmark(Operator): + bl_idname = "view3d.cursor_to_vr_landmark" + bl_label = "Cursor to VR Landmark" + bl_description = "Move the 3D Cursor to the selected VR Landmark" + bl_options = {'UNDO', 'REGISTER'} + + @classmethod + def poll(cls, context): + lm = properties.VRLandmark.get_selected_landmark(context) + if lm.type == 'SCENE_CAMERA': + return context.scene.camera is not None + elif lm.type == 'OBJECT': + return lm.base_pose_object is not None + + return True + + def execute(self, context): + scene = context.scene + lm = properties.VRLandmark.get_selected_landmark(context) + if lm.type == 'SCENE_CAMERA': + lm_pos = scene.camera.location + elif lm.type == 'OBJECT': + lm_pos = lm.base_pose_object.location + else: + lm_pos = lm.base_pose_location + scene.cursor.location = lm_pos + + return{'FINISHED'} + + +class VIEW3D_OT_add_camera_from_vr_landmark(Operator): + bl_idname = "view3d.add_camera_from_vr_landmark" + bl_label = "New Camera from VR Landmark" + bl_description = "Create a new Camera from the selected VR Landmark" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + scene = context.scene + lm = properties.VRLandmark.get_selected_landmark(context) + + cam = bpy.data.cameras.new("Camera_" + lm.name) + new_cam = bpy.data.objects.new("Camera_" + lm.name, cam) + scene.collection.objects.link(new_cam) + angle = lm.base_pose_angle + new_cam.location = lm.base_pose_location + new_cam.rotation_euler = (math.pi, 0, angle) + + return {'FINISHED'} + + +class VIEW3D_OT_camera_to_vr_landmark(Operator): + bl_idname = "view3d.camera_to_vr_landmark" + bl_label = "Scene Camera to VR Landmark" + bl_description = "Position the scene camera at the selected landmark" + bl_options = {'UNDO', 'REGISTER'} + + @classmethod + def poll(cls, context): + return context.scene.camera is not None + + def execute(self, context): + scene = context.scene + lm = properties.VRLandmark.get_selected_landmark(context) + + cam = scene.camera + angle = lm.base_pose_angle + cam.location = lm.base_pose_location + cam.rotation_euler = (math.pi / 2, 0, angle) + + return {'FINISHED'} + + +class VIEW3D_OT_vr_landmark_activate(Operator): + bl_idname = "view3d.vr_landmark_activate" + bl_label = "Activate VR Landmark" + bl_description = "Change to the selected VR landmark from the list" + bl_options = {'UNDO', 'REGISTER'} + + index: bpy.props.IntProperty( + name="Index", + options={'HIDDEN'}, + ) + + def execute(self, context): + scene = context.scene + + if self.index >= len(scene.vr_landmarks): + return {'CANCELLED'} + + scene.vr_landmarks_active = ( + self.index if self.properties.is_property_set( + "index") else scene.vr_landmarks_selected + ) + + return {'FINISHED'} + + +### Gizmos. +class VIEW3D_GT_vr_camera_cone(Gizmo): + bl_idname = "VIEW_3D_GT_vr_camera_cone" + + aspect = 1.0, 1.0 + + def draw(self, context): + if not hasattr(self, "frame_shape"): + aspect = self.aspect + + frame_shape_verts = ( + (-aspect[0], -aspect[1], -1.0), + (aspect[0], -aspect[1], -1.0), + (aspect[0], aspect[1], -1.0), + (-aspect[0], aspect[1], -1.0), + ) + lines_shape_verts = ( + (0.0, 0.0, 0.0), + frame_shape_verts[0], + (0.0, 0.0, 0.0), + frame_shape_verts[1], + (0.0, 0.0, 0.0), + frame_shape_verts[2], + (0.0, 0.0, 0.0), + frame_shape_verts[3], + ) + + self.frame_shape = self.new_custom_shape( + 'LINE_LOOP', frame_shape_verts) + self.lines_shape = self.new_custom_shape( + 'LINES', lines_shape_verts) + + # Ensure correct GL state (otherwise other gizmos might mess that up) + bgl.glLineWidth(1) + bgl.glEnable(bgl.GL_BLEND) + + self.draw_custom_shape(self.frame_shape) + self.draw_custom_shape(self.lines_shape) + + +class VIEW3D_GT_vr_controller_grip(Gizmo): + bl_idname = "VIEW_3D_GT_vr_controller_grip" + + def draw(self, context): + bgl.glLineWidth(1) + bgl.glEnable(bgl.GL_BLEND) + + self.color = 0.422, 0.438, 0.446 + self.draw_preset_circle(self.matrix_basis, axis='POS_X') + self.draw_preset_circle(self.matrix_basis, axis='POS_Y') + self.draw_preset_circle(self.matrix_basis, axis='POS_Z') + + +class VIEW3D_GT_vr_controller_aim(Gizmo): + bl_idname = "VIEW_3D_GT_vr_controller_aim" + + def draw(self, context): + bgl.glLineWidth(1) + bgl.glEnable(bgl.GL_BLEND) + + self.color = 1.0, 0.2, 0.322 + self.draw_preset_arrow(self.matrix_basis, axis='POS_X') + self.color = 0.545, 0.863, 0.0 + self.draw_preset_arrow(self.matrix_basis, axis='POS_Y') + self.color = 0.157, 0.565, 1.0 + self.draw_preset_arrow(self.matrix_basis, axis='POS_Z') + + +class VIEW3D_GGT_vr_viewer_pose(GizmoGroup): + bl_idname = "VIEW3D_GGT_vr_viewer_pose" + bl_label = "VR Viewer Pose Indicator" + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'} + + @classmethod + def poll(cls, context): + view3d = context.space_data + return ( + view3d.shading.vr_show_virtual_camera and + bpy.types.XrSessionState.is_running(context) and + not view3d.mirror_xr_session + ) + + @staticmethod + def _get_viewer_pose_matrix(context): + wm = context.window_manager + + loc = wm.xr_session_state.viewer_pose_location + rot = wm.xr_session_state.viewer_pose_rotation + + rotmat = Matrix.Identity(3) + rotmat.rotate(rot) + rotmat.resize_4x4() + transmat = Matrix.Translation(loc) + + return transmat @ rotmat + + def setup(self, context): + gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname) + gizmo.aspect = 1 / 3, 1 / 4 + + gizmo.color = gizmo.color_highlight = 0.2, 0.6, 1.0 + gizmo.alpha = 1.0 + + self.gizmo = gizmo + + def draw_prepare(self, context): + self.gizmo.matrix_basis = self._get_viewer_pose_matrix(context) + + +class VIEW3D_GGT_vr_controller_poses(GizmoGroup): + bl_idname = "VIEW3D_GGT_vr_controller_poses" + bl_label = "VR Controller Poses Indicator" + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'} + + @classmethod + def poll(cls, context): + view3d = context.space_data + return ( + view3d.shading.vr_show_controllers and + bpy.types.XrSessionState.is_running(context) and + not view3d.mirror_xr_session + ) + + @staticmethod + def _get_controller_pose_matrix(context, idx, is_grip, scale): + wm = context.window_manager + + loc = None + rot = None + if is_grip: + loc = wm.xr_session_state.controller_grip_location_get(context, idx) + rot = wm.xr_session_state.controller_grip_rotation_get(context, idx) + else: + loc = wm.xr_session_state.controller_aim_location_get(context, idx) + rot = wm.xr_session_state.controller_aim_rotation_get(context, idx) + + rotmat = Matrix.Identity(3) + rotmat.rotate(Quaternion(Vector(rot))) + rotmat.resize_4x4() + transmat = Matrix.Translation(loc) + scalemat = Matrix.Scale(scale, 4) + + return transmat @ rotmat @ scalemat + + def setup(self, context): + for idx in range(2): + self.gizmos.new(VIEW3D_GT_vr_controller_grip.bl_idname) + self.gizmos.new(VIEW3D_GT_vr_controller_aim.bl_idname) + + for gizmo in self.gizmos: + gizmo.aspect = 1 / 3, 1 / 4 + gizmo.color_highlight = 1.0, 1.0, 1.0 + gizmo.alpha = 1.0 + + def draw_prepare(self, context): + grip_idx = 0 + aim_idx = 0 + idx = 0 + scale = 1.0 + for gizmo in self.gizmos: + is_grip = (gizmo.bl_idname == VIEW3D_GT_vr_controller_grip.bl_idname) + if (is_grip): + idx = grip_idx + grip_idx += 1 + scale = 0.1 + else: + idx = aim_idx + aim_idx += 1 + scale = 0.5 + gizmo.matrix_basis = self._get_controller_pose_matrix(context, idx, is_grip, scale) + + +class VIEW3D_GGT_vr_landmarks(GizmoGroup): + bl_idname = "VIEW3D_GGT_vr_landmarks" + bl_label = "VR Landmark Indicators" + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', 'PERSISTENT', 'SCALE'} + + @classmethod + def poll(cls, context): + view3d = context.space_data + return ( + view3d.shading.vr_show_landmarks + ) + + def setup(self, context): + pass + + def draw_prepare(self, context): + # first delete the old gizmos + for g in self.gizmos: + self.gizmos.remove(g) + + scene = context.scene + landmarks = scene.vr_landmarks + + for lm in landmarks: + if ((lm.type == 'SCENE_CAMERA' and not scene.camera) or + (lm.type == 'OBJECT' and not lm.base_pose_object)): + continue + + gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname) + gizmo.aspect = 1 / 3, 1 / 4 + + gizmo.color = gizmo.color_highlight = 0.2, 1.0, 0.6 + gizmo.alpha = 1.0 + + self.gizmo = gizmo + + if lm.type == 'SCENE_CAMERA': + cam = scene.camera + lm_mat = cam.matrix_world if cam else Matrix.Identity(4) + elif lm.type == 'OBJECT': + lm_mat = lm.base_pose_object.matrix_world + else: + angle = lm.base_pose_angle + raw_rot = Euler((radians(90.0), 0, angle)) + + rotmat = Matrix.Identity(3) + rotmat.rotate(raw_rot) + rotmat.resize_4x4() + + transmat = Matrix.Translation(lm.base_pose_location) + + lm_mat = transmat @ rotmat + + self.gizmo.matrix_basis = lm_mat + + +classes = ( + VIEW3D_OT_vr_landmark_add, + VIEW3D_OT_vr_landmark_remove, + VIEW3D_OT_vr_landmark_activate, + VIEW3D_OT_vr_landmark_from_session, + VIEW3D_OT_add_camera_from_vr_landmark, + VIEW3D_OT_camera_to_vr_landmark, + VIEW3D_OT_vr_landmark_from_camera, + VIEW3D_OT_cursor_to_vr_landmark, + VIEW3D_OT_update_vr_landmark, + + VIEW3D_GT_vr_camera_cone, + VIEW3D_GT_vr_controller_grip, + VIEW3D_GT_vr_controller_aim, + VIEW3D_GGT_vr_viewer_pose, + VIEW3D_GGT_vr_controller_poses, + VIEW3D_GGT_vr_landmarks, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) diff --git a/viewport_vr_preview/properties.py b/viewport_vr_preview/properties.py new file mode 100644 index 00000000..03f03e8c --- /dev/null +++ b/viewport_vr_preview/properties.py @@ -0,0 +1,244 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import ( + PropertyGroup, +) +from bpy.app.handlers import persistent + + +### Landmarks. +@persistent +def vr_ensure_default_landmark(context: bpy.context): + # Ensure there's a default landmark (scene camera by default). + landmarks = bpy.context.scene.vr_landmarks + if not landmarks: + landmarks.add() + landmarks[0].type = 'SCENE_CAMERA' + + +def vr_landmark_active_type_update(self, context): + wm = context.window_manager + session_settings = wm.xr_session_settings + landmark_active = VRLandmark.get_active_landmark(context) + + # Update session's base pose type to the matching type. + if landmark_active.type == 'SCENE_CAMERA': + session_settings.base_pose_type = 'SCENE_CAMERA' + elif landmark_active.type == 'OBJECT': + session_settings.base_pose_type = 'OBJECT' + elif landmark_active.type == 'CUSTOM': + session_settings.base_pose_type = 'CUSTOM' + + +def vr_landmark_active_base_pose_object_update(self, context): + session_settings = context.window_manager.xr_session_settings + landmark_active = VRLandmark.get_active_landmark(context) + + # Update the anchor object to the (new) camera of this landmark. + session_settings.base_pose_object = landmark_active.base_pose_object + + +def vr_landmark_active_base_pose_location_update(self, context): + session_settings = context.window_manager.xr_session_settings + landmark_active = VRLandmark.get_active_landmark(context) + + session_settings.base_pose_location = landmark_active.base_pose_location + + +def vr_landmark_active_base_pose_angle_update(self, context): + session_settings = context.window_manager.xr_session_settings + landmark_active = VRLandmark.get_active_landmark(context) + + session_settings.base_pose_angle = landmark_active.base_pose_angle + + +def vr_landmark_active_base_scale_update(self, context): + session_settings = context.window_manager.xr_session_settings + landmark_active = VRLandmark.get_active_landmark(context) + + session_settings.base_scale = landmark_active.base_scale + + +def vr_landmark_type_update(self, context): + landmark_selected = VRLandmark.get_selected_landmark(context) + landmark_active = VRLandmark.get_active_landmark(context) + + # Don't allow non-trivial base scale for scene camera landmarks. + if landmark_selected.type == 'SCENE_CAMERA': + landmark_selected.base_scale = 1.0 + + # Only update session settings data if the changed landmark is actually + # the active one. + if landmark_active == landmark_selected: + vr_landmark_active_type_update(self, context) + + +def vr_landmark_base_pose_object_update(self, context): + landmark_selected = VRLandmark.get_selected_landmark(context) + landmark_active = VRLandmark.get_active_landmark(context) + + # Only update session settings data if the changed landmark is actually + # the active one. + if landmark_active == landmark_selected: + vr_landmark_active_base_pose_object_update(self, context) + + +def vr_landmark_base_pose_location_update(self, context): + landmark_selected = VRLandmark.get_selected_landmark(context) + landmark_active = VRLandmark.get_active_landmark(context) + + # Only update session settings data if the changed landmark is actually + # the active one. + if landmark_active == landmark_selected: + vr_landmark_active_base_pose_location_update(self, context) + + +def vr_landmark_base_pose_angle_update(self, context): + landmark_selected = VRLandmark.get_selected_landmark(context) + landmark_active = VRLandmark.get_active_landmark(context) + + # Only update session settings data if the changed landmark is actually + # the active one. + if landmark_active == landmark_selected: + vr_landmark_active_base_pose_angle_update(self, context) + + +def vr_landmark_base_scale_update(self, context): + landmark_selected = VRLandmark.get_selected_landmark(context) + landmark_active = VRLandmark.get_active_landmark(context) + + # Only update session settings data if the changed landmark is actually + # the active one. + if landmark_active == landmark_selected: + vr_landmark_active_base_scale_update(self, context) + + +def vr_landmark_active_update(self, context): + wm = context.window_manager + + vr_landmark_active_type_update(self, context) + vr_landmark_active_base_pose_object_update(self, context) + vr_landmark_active_base_pose_location_update(self, context) + vr_landmark_active_base_pose_angle_update(self, context) + vr_landmark_active_base_scale_update(self, context) + + if wm.xr_session_state: + wm.xr_session_state.reset_to_base_pose(context) + + +class VRLandmark(PropertyGroup): + name: bpy.props.StringProperty( + name="VR Landmark", + default="Landmark" + ) + type: bpy.props.EnumProperty( + name="Type", + items=[ + ('SCENE_CAMERA', "Scene Camera", + "Use scene's currently active camera to define the VR view base " + "location and rotation"), + ('OBJECT', "Custom Object", + "Use an existing object to define the VR view base location and " + "rotation"), + ('CUSTOM', "Custom Pose", + "Allow a manually defined position and rotation to be used as " + "the VR view base pose"), + ], + default='SCENE_CAMERA', + update=vr_landmark_type_update, + ) + base_pose_object: bpy.props.PointerProperty( + name="Object", + type=bpy.types.Object, + update=vr_landmark_base_pose_object_update, + ) + base_pose_location: bpy.props.FloatVectorProperty( + name="Base Pose Location", + subtype='TRANSLATION', + update=vr_landmark_base_pose_location_update, + ) + base_pose_angle: bpy.props.FloatProperty( + name="Base Pose Angle", + subtype='ANGLE', + update=vr_landmark_base_pose_angle_update, + ) + base_scale: bpy.props.FloatProperty( + name="Base Scale", + description="Viewer reference scale associated with this landmark", + default=1.0, + min=0.000001, + update=vr_landmark_base_scale_update, + ) + + @staticmethod + def get_selected_landmark(context): + scene = context.scene + landmarks = scene.vr_landmarks + + return ( + None if (len(landmarks) < + 1) else landmarks[scene.vr_landmarks_selected] + ) + + @staticmethod + def get_active_landmark(context): + scene = context.scene + landmarks = scene.vr_landmarks + + return ( + None if (len(landmarks) < + 1) else landmarks[scene.vr_landmarks_active] + ) + + +classes = ( + VRLandmark, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Scene.vr_landmarks = bpy.props.CollectionProperty( + name="Landmark", + type=VRLandmark, + ) + bpy.types.Scene.vr_landmarks_selected = bpy.props.IntProperty( + name="Selected Landmark" + ) + bpy.types.Scene.vr_landmarks_active = bpy.props.IntProperty( + update=vr_landmark_active_update, + ) + + bpy.app.handlers.load_post.append(vr_ensure_default_landmark) + + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + + del bpy.types.Scene.vr_landmarks + del bpy.types.Scene.vr_landmarks_selected + del bpy.types.Scene.vr_landmarks_active + + bpy.app.handlers.load_post.remove(vr_ensure_default_landmark) diff --git a/viewport_vr_preview/versioning.py b/viewport_vr_preview/versioning.py new file mode 100644 index 00000000..bf7e891a --- /dev/null +++ b/viewport_vr_preview/versioning.py @@ -0,0 +1,54 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# Update Blender version this action map was written in: +# +# When the version is ``(0, 0, 0)``, the action map being loaded didn't contain any versioning information. +# This will older than ``(3, 0, 0)``. + +def actionconfig_update(actionconfig_data, actionconfig_version): + from bpy.app import version_file as blender_version + if actionconfig_version >= blender_version: + return actionconfig_data + +## # Version the action map. +## import copy +## has_copy = False +## +## if actionconfig_version <= (3, 0, 0): +## # Only copy once. +## if not has_copy: +## actionconfig_data = copy.deepcopy(actionconfig_data) +## has_copy = True +## +## for (am_name, am_content) in actionconfig_data: +## # Apply action map updates. +## +## am_items = am_content["items"] +## +## for (ami_name, ami_args, ami_data, ami_content) in am_items +## # Apply action map item updates. +## +## ami_bindings = ami_content["bindings"] +## +## for (amb_name, amb_args) in ami_bindings: +## # Apply action map binding updates. + + return actionconfig_data |