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

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'viewport_vr_preview.py')
-rw-r--r--viewport_vr_preview.py325
1 files changed, 299 insertions, 26 deletions
diff --git a/viewport_vr_preview.py b/viewport_vr_preview.py
index 8dc865f5..44e8897b 100644
--- a/viewport_vr_preview.py
+++ b/viewport_vr_preview.py
@@ -22,6 +22,11 @@ import bpy
from bpy.types import (
Gizmo,
GizmoGroup,
+ PropertyGroup,
+ UIList,
+ Menu,
+ Panel,
+ Operator,
)
from bpy.props import (
CollectionProperty,
@@ -32,9 +37,9 @@ from bpy.app.handlers import persistent
bl_info = {
"name": "VR Scene Inspection",
- "author": "Julian Eisel (Severin)",
- "version": (0, 2, 0),
- "blender": (2, 83, 0),
+ "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)"),
@@ -65,8 +70,8 @@ def xr_landmark_active_type_update(self, context):
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'
+ elif landmark_active.type == 'CUSTOM':
+ session_settings.base_pose_type = 'CUSTOM'
def xr_landmark_active_camera_update(self, context):
@@ -144,10 +149,24 @@ def xr_landmark_active_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)
+ wm.xr_session_state.reset_to_base_pose(context)
-class VRLandmark(bpy.types.PropertyGroup):
+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"
@@ -161,11 +180,9 @@ class VRLandmark(bpy.types.PropertyGroup):
('USER_CAMERA', "Custom Camera",
"Use an existing camera to define the VR view base location and "
"rotation"),
- # Custom base poses work, but it's uncertain if they are really
- # needed. Disabled for now.
- # ('CUSTOM', "Custom Pose",
- # "Allow a manually definied position and rotation to be used as "
- # "the VR view base pose"),
+ ('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,
@@ -209,7 +226,7 @@ class VRLandmark(bpy.types.PropertyGroup):
)
-class VIEW3D_UL_vr_landmarks(bpy.types.UIList):
+class VIEW3D_UL_vr_landmarks(UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data,
_active_propname, index):
landmark = item
@@ -227,7 +244,7 @@ class VIEW3D_UL_vr_landmarks(bpy.types.UIList):
props.index = index
-class VIEW3D_PT_vr_landmarks(bpy.types.Panel):
+class VIEW3D_PT_vr_landmarks(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "VR"
@@ -250,20 +267,23 @@ class VIEW3D_PT_vr_landmarks(bpy.types.Panel):
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")
+ 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(bpy.types.Panel):
+class VIEW3D_PT_vr_session_view(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "VR"
@@ -285,7 +305,7 @@ class VIEW3D_PT_vr_session_view(bpy.types.Panel):
col.prop(session_settings, "clip_end", text="End")
-class VIEW3D_PT_vr_session(bpy.types.Panel):
+class VIEW3D_PT_vr_session(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "VR"
@@ -295,7 +315,8 @@ class VIEW3D_PT_vr_session(bpy.types.Panel):
layout = self.layout
session_settings = context.window_manager.xr_session_settings
- layout.use_property_split = False
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
is_session_running = bpy.types.XrSessionState.is_running(context)
@@ -313,7 +334,22 @@ class VIEW3D_PT_vr_session(bpy.types.Panel):
layout.prop(session_settings, "use_positional_tracking")
-class VIEW3D_OT_vr_landmark_add(bpy.types.Operator):
+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"
@@ -331,7 +367,93 @@ class VIEW3D_OT_vr_landmark_add(bpy.types.Operator):
return {'FINISHED'}
-class VIEW3D_OT_vr_landmark_remove(bpy.types.Operator):
+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)
+
+ 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 = 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"
@@ -350,7 +472,83 @@ class VIEW3D_OT_vr_landmark_remove(bpy.types.Operator):
return {'FINISHED'}
-class VIEW3D_OT_vr_landmark_activate(bpy.types.Operator):
+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"
@@ -375,7 +573,7 @@ class VIEW3D_OT_vr_landmark_activate(bpy.types.Operator):
return {'FINISHED'}
-class VIEW3D_PT_vr_viewport_feedback(bpy.types.Panel):
+class VIEW3D_PT_vr_viewport_feedback(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "VR"
@@ -394,6 +592,7 @@ class VIEW3D_PT_vr_viewport_feedback(bpy.types.Panel):
layout.separator()
layout.prop(view3d.shading, "vr_show_virtual_camera")
+ layout.prop(view3d.shading, "vr_show_landmarks")
layout.prop(view3d, "mirror_xr_session")
@@ -483,6 +682,66 @@ class VIEW3D_GGT_vr_viewer_pose(GizmoGroup):
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,
@@ -491,18 +750,27 @@ classes = (
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:
@@ -523,12 +791,16 @@ def register():
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:
@@ -538,6 +810,7 @@ def unregister():
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)