Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / viewport_vr_preview.py
blob07f34756356e0c90bf698c95723333ad0dc69310
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 import bpy
22 from bpy.types import (
23 Gizmo,
24 GizmoGroup,
25 PropertyGroup,
26 UIList,
27 Menu,
28 Panel,
29 Operator,
31 from bpy.props import (
32 CollectionProperty,
33 IntProperty,
34 BoolProperty,
36 from bpy.app.handlers import persistent
38 bl_info = {
39 "name": "VR Scene Inspection",
40 "author": "Julian Eisel (Severin), Sebastian Koenig",
41 "version": (0, 9, 0),
42 "blender": (2, 90, 0),
43 "location": "3D View > Sidebar > VR",
44 "description": ("View the viewport with virtual reality glasses "
45 "(head-mounted displays)"),
46 "support": "OFFICIAL",
47 "warning": "This is an early, limited preview of in development "
48 "VR support for Blender.",
49 "doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/vr_scene_inspection.html",
50 "category": "3D View",
54 @persistent
55 def ensure_default_vr_landmark(context: bpy.context):
56 # Ensure there's a default landmark (scene camera by default).
57 landmarks = bpy.context.scene.vr_landmarks
58 if not landmarks:
59 landmarks.add()
60 landmarks[0].type = 'SCENE_CAMERA'
63 def xr_landmark_active_type_update(self, context):
64 wm = context.window_manager
65 session_settings = wm.xr_session_settings
66 landmark_active = VRLandmark.get_active_landmark(context)
68 # Update session's base pose type to the matching type.
69 if landmark_active.type == 'SCENE_CAMERA':
70 session_settings.base_pose_type = 'SCENE_CAMERA'
71 elif landmark_active.type == 'USER_CAMERA':
72 session_settings.base_pose_type = 'OBJECT'
73 elif landmark_active.type == 'CUSTOM':
74 session_settings.base_pose_type = 'CUSTOM'
77 def xr_landmark_active_camera_update(self, context):
78 session_settings = context.window_manager.xr_session_settings
79 landmark_active = VRLandmark.get_active_landmark(context)
81 # Update the anchor object to the (new) camera of this landmark.
82 session_settings.base_pose_object = landmark_active.base_pose_camera
85 def xr_landmark_active_base_pose_location_update(self, context):
86 session_settings = context.window_manager.xr_session_settings
87 landmark_active = VRLandmark.get_active_landmark(context)
89 session_settings.base_pose_location = landmark_active.base_pose_location
92 def xr_landmark_active_base_pose_angle_update(self, context):
93 session_settings = context.window_manager.xr_session_settings
94 landmark_active = VRLandmark.get_active_landmark(context)
96 session_settings.base_pose_angle = landmark_active.base_pose_angle
99 def xr_landmark_type_update(self, context):
100 landmark_selected = VRLandmark.get_selected_landmark(context)
101 landmark_active = VRLandmark.get_active_landmark(context)
103 # Only update session settings data if the changed landmark is actually
104 # the active one.
105 if landmark_active == landmark_selected:
106 xr_landmark_active_type_update(self, context)
109 def xr_landmark_camera_update(self, context):
110 landmark_selected = VRLandmark.get_selected_landmark(context)
111 landmark_active = VRLandmark.get_active_landmark(context)
113 # Only update session settings data if the changed landmark is actually
114 # the active one.
115 if landmark_active == landmark_selected:
116 xr_landmark_active_camera_update(self, context)
119 def xr_landmark_base_pose_location_update(self, context):
120 landmark_selected = VRLandmark.get_selected_landmark(context)
121 landmark_active = VRLandmark.get_active_landmark(context)
123 # Only update session settings data if the changed landmark is actually
124 # the active one.
125 if landmark_active == landmark_selected:
126 xr_landmark_active_base_pose_location_update(self, context)
129 def xr_landmark_base_pose_angle_update(self, context):
130 landmark_selected = VRLandmark.get_selected_landmark(context)
131 landmark_active = VRLandmark.get_active_landmark(context)
133 # Only update session settings data if the changed landmark is actually
134 # the active one.
135 if landmark_active == landmark_selected:
136 xr_landmark_active_base_pose_angle_update(self, context)
139 def xr_landmark_camera_object_poll(self, object):
140 return object.type == 'CAMERA'
143 def xr_landmark_active_update(self, context):
144 wm = context.window_manager
146 xr_landmark_active_type_update(self, context)
147 xr_landmark_active_camera_update(self, context)
148 xr_landmark_active_base_pose_location_update(self, context)
149 xr_landmark_active_base_pose_angle_update(self, context)
151 if wm.xr_session_state:
152 wm.xr_session_state.reset_to_base_pose(context)
155 class VIEW3D_MT_landmark_menu(Menu):
156 bl_label = "Landmark Controls"
158 def draw(self, _context):
159 layout = self.layout
161 layout.operator("view3d.vr_landmark_from_camera")
162 layout.operator("view3d.update_vr_landmark")
163 layout.separator()
164 layout.operator("view3d.cursor_to_vr_landmark")
165 layout.operator("view3d.camera_to_vr_landmark")
166 layout.operator("view3d.add_camera_from_vr_landmark")
169 class VRLandmark(PropertyGroup):
170 name: bpy.props.StringProperty(
171 name="VR Landmark",
172 default="Landmark"
174 type: bpy.props.EnumProperty(
175 name="Type",
176 items=[
177 ('SCENE_CAMERA', "Scene Camera",
178 "Use scene's currently active camera to define the VR view base "
179 "location and rotation"),
180 ('USER_CAMERA', "Custom Camera",
181 "Use an existing camera to define the VR view base location and "
182 "rotation"),
183 ('CUSTOM', "Custom Pose",
184 "Allow a manually defined position and rotation to be used as "
185 "the VR view base pose"),
187 default='SCENE_CAMERA',
188 update=xr_landmark_type_update,
190 base_pose_camera: bpy.props.PointerProperty(
191 name="Camera",
192 type=bpy.types.Object,
193 poll=xr_landmark_camera_object_poll,
194 update=xr_landmark_camera_update,
196 base_pose_location: bpy.props.FloatVectorProperty(
197 name="Base Pose Location",
198 subtype='TRANSLATION',
199 update=xr_landmark_base_pose_location_update,
202 base_pose_angle: bpy.props.FloatProperty(
203 name="Base Pose Angle",
204 subtype='ANGLE',
205 update=xr_landmark_base_pose_angle_update,
208 @staticmethod
209 def get_selected_landmark(context):
210 scene = context.scene
211 landmarks = scene.vr_landmarks
213 return (
214 None if (len(landmarks) <
215 1) else landmarks[scene.vr_landmarks_selected]
218 @staticmethod
219 def get_active_landmark(context):
220 scene = context.scene
221 landmarks = scene.vr_landmarks
223 return (
224 None if (len(landmarks) <
225 1) else landmarks[scene.vr_landmarks_active]
229 class VIEW3D_UL_vr_landmarks(UIList):
230 def draw_item(self, context, layout, _data, item, icon, _active_data,
231 _active_propname, index):
232 landmark = item
233 landmark_active_idx = context.scene.vr_landmarks_active
235 layout.emboss = 'NONE'
237 layout.prop(landmark, "name", text="")
239 icon = (
240 'RADIOBUT_ON' if (index == landmark_active_idx) else 'RADIOBUT_OFF'
242 props = layout.operator(
243 "view3d.vr_landmark_activate", text="", icon=icon)
244 props.index = index
247 class VIEW3D_PT_vr_landmarks(Panel):
248 bl_space_type = 'VIEW_3D'
249 bl_region_type = 'UI'
250 bl_category = "VR"
251 bl_label = "Landmarks"
252 bl_options = {'DEFAULT_CLOSED'}
254 def draw(self, context):
255 layout = self.layout
256 scene = context.scene
257 landmark_selected = VRLandmark.get_selected_landmark(context)
259 layout.use_property_split = True
260 layout.use_property_decorate = False # No animation.
262 row = layout.row()
264 row.template_list("VIEW3D_UL_vr_landmarks", "", scene, "vr_landmarks",
265 scene, "vr_landmarks_selected", rows=3)
267 col = row.column(align=True)
268 col.operator("view3d.vr_landmark_add", icon='ADD', text="")
269 col.operator("view3d.vr_landmark_remove", icon='REMOVE', text="")
270 col.operator("view3d.vr_landmark_from_session", icon='PLUS', text="")
272 col.menu("VIEW3D_MT_landmark_menu", icon='DOWNARROW_HLT', text="")
274 if landmark_selected:
275 layout.prop(landmark_selected, "type")
277 if landmark_selected.type == 'USER_CAMERA':
278 layout.prop(landmark_selected, "base_pose_camera")
279 elif landmark_selected.type == 'CUSTOM':
280 layout.prop(landmark_selected,
281 "base_pose_location", text="Location")
282 layout.prop(landmark_selected,
283 "base_pose_angle", text="Angle")
286 class VIEW3D_PT_vr_session_view(Panel):
287 bl_space_type = 'VIEW_3D'
288 bl_region_type = 'UI'
289 bl_category = "VR"
290 bl_label = "View"
292 def draw(self, context):
293 layout = self.layout
294 session_settings = context.window_manager.xr_session_settings
296 layout.use_property_split = True
297 layout.use_property_decorate = False # No animation.
299 col = layout.column(align=True, heading="Show")
300 col.prop(session_settings, "show_floor", text="Floor")
301 col.prop(session_settings, "show_annotation", text="Annotations")
303 col = layout.column(align=True)
304 col.prop(session_settings, "clip_start", text="Clip Start")
305 col.prop(session_settings, "clip_end", text="End")
308 class VIEW3D_PT_vr_session(Panel):
309 bl_space_type = 'VIEW_3D'
310 bl_region_type = 'UI'
311 bl_category = "VR"
312 bl_label = "VR Session"
314 def draw(self, context):
315 layout = self.layout
316 session_settings = context.window_manager.xr_session_settings
318 layout.use_property_split = True
319 layout.use_property_decorate = False # No animation.
321 is_session_running = bpy.types.XrSessionState.is_running(context)
323 # Using SNAP_FACE because it looks like a stop icon -- I shouldn't
324 # have commit rights...
325 toggle_info = (
326 ("Start VR Session", 'PLAY') if not is_session_running else (
327 "Stop VR Session", 'SNAP_FACE')
329 layout.operator("wm.xr_session_toggle",
330 text=toggle_info[0], icon=toggle_info[1])
332 layout.separator()
334 layout.prop(session_settings, "use_positional_tracking")
335 layout.prop(session_settings, "use_absolute_tracking")
338 class VIEW3D_PT_vr_info(bpy.types.Panel):
339 bl_space_type = 'VIEW_3D'
340 bl_region_type = 'UI'
341 bl_category = "VR"
342 bl_label = "VR Info"
344 @classmethod
345 def poll(cls, context):
346 return not bpy.app.build_options.xr_openxr
348 def draw(self, context):
349 layout = self.layout
350 layout.label(icon='ERROR', text="Built without VR/OpenXR features")
353 class VIEW3D_OT_vr_landmark_add(Operator):
354 bl_idname = "view3d.vr_landmark_add"
355 bl_label = "Add VR Landmark"
356 bl_description = "Add a new VR landmark to the list and select it"
357 bl_options = {'UNDO', 'REGISTER'}
359 def execute(self, context):
360 scene = context.scene
361 landmarks = scene.vr_landmarks
363 landmarks.add()
365 # select newly created set
366 scene.vr_landmarks_selected = len(landmarks) - 1
368 return {'FINISHED'}
371 class VIEW3D_OT_vr_landmark_from_camera(Operator):
372 bl_idname = "view3d.vr_landmark_from_camera"
373 bl_label = "Add VR Landmark from camera"
374 bl_description = "Add a new VR landmark from the active camera object to the list and select it"
375 bl_options = {'UNDO', 'REGISTER'}
377 @classmethod
378 def poll(cls, context):
379 cam_selected = False
381 vl_objects = bpy.context.view_layer.objects
382 if vl_objects.active and vl_objects.active.type == 'CAMERA':
383 cam_selected = True
384 return cam_selected
386 def execute(self, context):
387 scene = context.scene
388 landmarks = scene.vr_landmarks
389 cam = context.view_layer.objects.active
390 lm = landmarks.add()
391 lm.type = 'USER_CAMERA'
392 lm.base_pose_camera = cam
393 lm.name = "LM_" + cam.name
395 # select newly created set
396 scene.vr_landmarks_selected = len(landmarks) - 1
398 return {'FINISHED'}
401 class VIEW3D_OT_vr_landmark_from_session(Operator):
402 bl_idname = "view3d.vr_landmark_from_session"
403 bl_label = "Add VR Landmark from session"
404 bl_description = "Add VR landmark from the viewer pose of the running VR session to the list and select it"
405 bl_options = {'UNDO', 'REGISTER'}
407 @classmethod
408 def poll(cls, context):
409 return bpy.types.XrSessionState.is_running(context)
411 @staticmethod
412 def _calc_landmark_angle_from_viewer_rotation(rot):
413 from mathutils import Vector
415 # We want an angle around Z based on the current viewer rotation. Idea
416 # is to create a vector from the viewer rotation, project that onto a
417 # Z-Up plane and use the resulting vector to get an angle around Z.
419 view_rot_vec = Vector((0, 0, 1))
420 view_rot_vec.rotate(rot)
421 angle_vec = view_rot_vec - view_rot_vec.project(Vector((0, 0, 1)))
423 # We could probably use a 3D version of Vector.angle_signed() here, but
424 # that's not available. So manually calculate it via a quaternion delta.
425 forward_vec = Vector((0, -1, 0))
426 diff = angle_vec.rotation_difference(forward_vec)
428 return diff.angle * -diff.axis[2]
430 def execute(self, context):
431 scene = context.scene
432 landmarks = scene.vr_landmarks
433 wm = context.window_manager
435 lm = landmarks.add()
436 lm.type = 'CUSTOM'
437 scene.vr_landmarks_selected = len(landmarks) - 1
439 loc = wm.xr_session_state.viewer_pose_location
440 rot = wm.xr_session_state.viewer_pose_rotation
441 angle = self._calc_landmark_angle_from_viewer_rotation(rot)
443 lm.base_pose_location = loc
444 lm.base_pose_angle = angle
446 return {'FINISHED'}
449 class VIEW3D_OT_update_vr_landmark(Operator):
450 bl_idname = "view3d.update_vr_landmark"
451 bl_label = "Update Custom VR Landmark"
452 bl_description = "Update the selected landmark from the current viewer pose in the VR session"
453 bl_options = {'UNDO', 'REGISTER'}
455 @classmethod
456 def poll(cls, context):
457 selected_landmark = VRLandmark.get_selected_landmark(context)
458 return bpy.types.XrSessionState.is_running(context) and selected_landmark.type == 'CUSTOM'
460 def execute(self, context):
461 wm = context.window_manager
463 lm = VRLandmark.get_selected_landmark(context)
465 loc = wm.xr_session_state.viewer_pose_location
466 rot = wm.xr_session_state.viewer_pose_rotation.to_euler()
468 lm.base_pose_location = loc
469 lm.base_pose_angle = rot
471 # Re-activate the landmark to trigger viewer reset and flush landmark settings to the session settings.
472 xr_landmark_active_update(None, context)
474 return {'FINISHED'}
477 class VIEW3D_OT_vr_landmark_remove(Operator):
478 bl_idname = "view3d.vr_landmark_remove"
479 bl_label = "Remove VR Landmark"
480 bl_description = "Delete the selected VR landmark from the list"
481 bl_options = {'UNDO', 'REGISTER'}
483 def execute(self, context):
484 scene = context.scene
485 landmarks = scene.vr_landmarks
487 if len(landmarks) > 1:
488 landmark_selected_idx = scene.vr_landmarks_selected
489 landmarks.remove(landmark_selected_idx)
491 scene.vr_landmarks_selected -= 1
493 return {'FINISHED'}
496 class VIEW3D_OT_cursor_to_vr_landmark(Operator):
497 bl_idname = "view3d.cursor_to_vr_landmark"
498 bl_label = "Cursor to VR Landmark"
499 bl_description = "Move the 3D Cursor to the selected VR Landmark"
500 bl_options = {'UNDO', 'REGISTER'}
502 @classmethod
503 def poll(cls, context):
504 lm = VRLandmark.get_selected_landmark(context)
505 if lm.type == 'SCENE_CAMERA':
506 return context.scene.camera is not None
507 elif lm.type == 'USER_CAMERA':
508 return lm.base_pose_camera is not None
510 return True
512 def execute(self, context):
513 scene = context.scene
514 lm = VRLandmark.get_selected_landmark(context)
515 if lm.type == 'SCENE_CAMERA':
516 lm_pos = scene.camera.location
517 elif lm.type == 'USER_CAMERA':
518 lm_pos = lm.base_pose_camera.location
519 else:
520 lm_pos = lm.base_pose_location
521 scene.cursor.location = lm_pos
523 return{'FINISHED'}
526 class VIEW3d_OT_add_camera_from_vr_landmark(Operator):
527 bl_idname = "view3d.add_camera_from_vr_landmark"
528 bl_label = "New Camera from VR Landmark"
529 bl_description = "Create a new Camera from the selected VR Landmark"
530 bl_options = {'UNDO', 'REGISTER'}
532 def execute(self, context):
533 import math
535 scene = context.scene
536 lm = VRLandmark.get_selected_landmark(context)
538 cam = bpy.data.cameras.new("Camera_" + lm.name)
539 new_cam = bpy.data.objects.new("Camera_" + lm.name, cam)
540 scene.collection.objects.link(new_cam)
541 angle = lm.base_pose_angle
542 new_cam.location = lm.base_pose_location
543 new_cam.rotation_euler = (math.pi, 0, angle)
545 return {'FINISHED'}
548 class VIEW3D_OT_camera_to_vr_landmark(Operator):
549 bl_idname = "view3d.camera_to_vr_landmark"
550 bl_label = "Scene Camera to VR Landmark"
551 bl_description = "Position the scene camera at the selected landmark"
552 bl_options = {'UNDO', 'REGISTER'}
554 @classmethod
555 def poll(cls, context):
556 return context.scene.camera is not None
558 def execute(self, context):
559 import math
561 scene = context.scene
562 lm = VRLandmark.get_selected_landmark(context)
564 cam = scene.camera
565 angle = lm.base_pose_angle
566 cam.location = lm.base_pose_location
567 cam.rotation_euler = (math.pi / 2, 0, angle)
569 return {'FINISHED'}
572 class VIEW3D_OT_vr_landmark_activate(Operator):
573 bl_idname = "view3d.vr_landmark_activate"
574 bl_label = "Activate VR Landmark"
575 bl_description = "Change to the selected VR landmark from the list"
576 bl_options = {'UNDO', 'REGISTER'}
578 index: IntProperty(
579 name="Index",
580 options={'HIDDEN'},
583 def execute(self, context):
584 scene = context.scene
586 if self.index >= len(scene.vr_landmarks):
587 return {'CANCELLED'}
589 scene.vr_landmarks_active = (
590 self.index if self.properties.is_property_set(
591 "index") else scene.vr_landmarks_selected
594 return {'FINISHED'}
597 class VIEW3D_PT_vr_viewport_feedback(Panel):
598 bl_space_type = 'VIEW_3D'
599 bl_region_type = 'UI'
600 bl_category = "VR"
601 bl_label = "Viewport Feedback"
602 bl_options = {'DEFAULT_CLOSED'}
604 def draw(self, context):
605 layout = self.layout
606 view3d = context.space_data
608 col = layout.column(align=True)
609 col.label(icon='ERROR', text="Note:")
610 col.label(text="Settings here may have a significant")
611 col.label(text="performance impact!")
613 layout.separator()
615 layout.prop(view3d.shading, "vr_show_virtual_camera")
616 layout.prop(view3d.shading, "vr_show_landmarks")
617 layout.prop(view3d, "mirror_xr_session")
620 class VIEW3D_GT_vr_camera_cone(Gizmo):
621 bl_idname = "VIEW_3D_GT_vr_camera_cone"
623 aspect = 1.0, 1.0
625 def draw(self, context):
626 import bgl
628 if not hasattr(self, "frame_shape"):
629 aspect = self.aspect
631 frame_shape_verts = (
632 (-aspect[0], -aspect[1], -1.0),
633 (aspect[0], -aspect[1], -1.0),
634 (aspect[0], aspect[1], -1.0),
635 (-aspect[0], aspect[1], -1.0),
637 lines_shape_verts = (
638 (0.0, 0.0, 0.0),
639 frame_shape_verts[0],
640 (0.0, 0.0, 0.0),
641 frame_shape_verts[1],
642 (0.0, 0.0, 0.0),
643 frame_shape_verts[2],
644 (0.0, 0.0, 0.0),
645 frame_shape_verts[3],
648 self.frame_shape = self.new_custom_shape(
649 'LINE_LOOP', frame_shape_verts)
650 self.lines_shape = self.new_custom_shape(
651 'LINES', lines_shape_verts)
653 # Ensure correct GL state (otherwise other gizmos might mess that up)
654 bgl.glLineWidth(1)
655 bgl.glEnable(bgl.GL_BLEND)
657 self.draw_custom_shape(self.frame_shape)
658 self.draw_custom_shape(self.lines_shape)
661 class VIEW3D_GGT_vr_viewer_pose(GizmoGroup):
662 bl_idname = "VIEW3D_GGT_vr_viewer_pose"
663 bl_label = "VR Viewer Pose Indicator"
664 bl_space_type = 'VIEW_3D'
665 bl_region_type = 'WINDOW'
666 bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'}
668 @classmethod
669 def poll(cls, context):
670 view3d = context.space_data
671 return (
672 view3d.shading.vr_show_virtual_camera and
673 bpy.types.XrSessionState.is_running(context) and
674 not view3d.mirror_xr_session
677 @staticmethod
678 def _get_viewer_pose_matrix(context):
679 from mathutils import Matrix, Quaternion
681 wm = context.window_manager
683 loc = wm.xr_session_state.viewer_pose_location
684 rot = wm.xr_session_state.viewer_pose_rotation
686 rotmat = Matrix.Identity(3)
687 rotmat.rotate(rot)
688 rotmat.resize_4x4()
689 transmat = Matrix.Translation(loc)
691 return transmat @ rotmat
693 def setup(self, context):
694 gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname)
695 gizmo.aspect = 1 / 3, 1 / 4
697 gizmo.color = gizmo.color_highlight = 0.2, 0.6, 1.0
698 gizmo.alpha = 1.0
700 self.gizmo = gizmo
702 def draw_prepare(self, context):
703 self.gizmo.matrix_basis = self._get_viewer_pose_matrix(context)
706 class VIEW3D_GGT_vr_landmarks(GizmoGroup):
707 bl_idname = "VIEW3D_GGT_vr_landmarks"
708 bl_label = "VR Landmark Indicators"
709 bl_space_type = 'VIEW_3D'
710 bl_region_type = 'WINDOW'
711 bl_options = {'3D', 'PERSISTENT', 'SCALE'}
713 @classmethod
714 def poll(cls, context):
715 view3d = context.space_data
716 return (
717 view3d.shading.vr_show_landmarks
720 def setup(self, context):
721 pass
723 def draw_prepare(self, context):
724 # first delete the old gizmos
725 for g in self.gizmos:
726 self.gizmos.remove(g)
728 from math import radians
729 from mathutils import Matrix, Euler
730 scene = context.scene
731 landmarks = scene.vr_landmarks
733 for lm in landmarks:
734 if ((lm.type == 'SCENE_CAMERA' and not scene.camera) or
735 (lm.type == 'USER_CAMERA' and not lm.base_pose_camera)):
736 continue
738 gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname)
739 gizmo.aspect = 1 / 3, 1 / 4
741 gizmo.color = gizmo.color_highlight = 0.2, 1.0, 0.6
742 gizmo.alpha = 1.0
744 self.gizmo = gizmo
746 if lm.type == 'SCENE_CAMERA':
747 cam = scene.camera
748 lm_mat = cam.matrix_world if cam else Matrix.Identity(4)
749 elif lm.type == 'USER_CAMERA':
750 lm_mat = lm.base_pose_camera.matrix_world
751 else:
752 angle = lm.base_pose_angle
753 raw_rot = Euler((radians(90.0), 0, angle))
755 rotmat = Matrix.Identity(3)
756 rotmat.rotate(raw_rot)
757 rotmat.resize_4x4()
759 transmat = Matrix.Translation(lm.base_pose_location)
761 lm_mat = transmat @ rotmat
763 self.gizmo.matrix_basis = lm_mat
766 classes = (
767 VIEW3D_PT_vr_session,
768 VIEW3D_PT_vr_session_view,
769 VIEW3D_PT_vr_landmarks,
770 VIEW3D_PT_vr_viewport_feedback,
772 VRLandmark,
773 VIEW3D_UL_vr_landmarks,
774 VIEW3D_MT_landmark_menu,
776 VIEW3D_OT_vr_landmark_add,
777 VIEW3D_OT_vr_landmark_remove,
778 VIEW3D_OT_vr_landmark_activate,
779 VIEW3D_OT_vr_landmark_from_session,
780 VIEW3d_OT_add_camera_from_vr_landmark,
781 VIEW3D_OT_camera_to_vr_landmark,
782 VIEW3D_OT_vr_landmark_from_camera,
783 VIEW3D_OT_cursor_to_vr_landmark,
784 VIEW3D_OT_update_vr_landmark,
786 VIEW3D_GT_vr_camera_cone,
787 VIEW3D_GGT_vr_viewer_pose,
788 VIEW3D_GGT_vr_landmarks,
792 def register():
793 if not bpy.app.build_options.xr_openxr:
794 bpy.utils.register_class(VIEW3D_PT_vr_info)
795 return
797 for cls in classes:
798 bpy.utils.register_class(cls)
800 bpy.types.Scene.vr_landmarks = CollectionProperty(
801 name="Landmark",
802 type=VRLandmark,
804 bpy.types.Scene.vr_landmarks_selected = IntProperty(
805 name="Selected Landmark"
807 bpy.types.Scene.vr_landmarks_active = IntProperty(
808 update=xr_landmark_active_update,
810 # View3DShading is the only per 3D-View struct with custom property
811 # support, so "abusing" that to get a per 3D-View option.
812 bpy.types.View3DShading.vr_show_virtual_camera = BoolProperty(
813 name="Show VR Camera"
815 bpy.types.View3DShading.vr_show_landmarks = BoolProperty(
816 name="Show Landmarks"
819 bpy.app.handlers.load_post.append(ensure_default_vr_landmark)
822 def unregister():
823 if not bpy.app.build_options.xr_openxr:
824 bpy.utils.unregister_class(VIEW3D_PT_vr_info)
825 return
827 for cls in classes:
828 bpy.utils.unregister_class(cls)
830 del bpy.types.Scene.vr_landmarks
831 del bpy.types.Scene.vr_landmarks_selected
832 del bpy.types.Scene.vr_landmarks_active
833 del bpy.types.View3DShading.vr_show_virtual_camera
834 del bpy.types.View3DShading.vr_show_landmarks
836 bpy.app.handlers.load_post.remove(ensure_default_vr_landmark)
839 if __name__ == "__main__":
840 register()