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 #####
22 from bpy
.types
import (
31 from bpy
.props
import (
36 from bpy
.app
.handlers
import persistent
39 "name": "VR Scene Inspection",
40 "author": "Julian Eisel (Severin), Sebastian Koenig",
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",
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
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
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
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
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
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
):
161 layout
.operator("view3d.vr_landmark_from_camera")
162 layout
.operator("view3d.update_vr_landmark")
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(
174 type: bpy
.props
.EnumProperty(
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 "
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(
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",
205 update
=xr_landmark_base_pose_angle_update
,
209 def get_selected_landmark(context
):
210 scene
= context
.scene
211 landmarks
= scene
.vr_landmarks
214 None if (len(landmarks
) <
215 1) else landmarks
[scene
.vr_landmarks_selected
]
219 def get_active_landmark(context
):
220 scene
= context
.scene
221 landmarks
= scene
.vr_landmarks
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
):
233 landmark_active_idx
= context
.scene
.vr_landmarks_active
235 layout
.emboss
= 'NONE'
237 layout
.prop(landmark
, "name", text
="")
240 'RADIOBUT_ON' if (index
== landmark_active_idx
) else 'RADIOBUT_OFF'
242 props
= layout
.operator(
243 "view3d.vr_landmark_activate", text
="", icon
=icon
)
247 class VIEW3D_PT_vr_landmarks(Panel
):
248 bl_space_type
= 'VIEW_3D'
249 bl_region_type
= 'UI'
251 bl_label
= "Landmarks"
252 bl_options
= {'DEFAULT_CLOSED'}
254 def draw(self
, context
):
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.
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'
292 def draw(self
, context
):
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'
312 bl_label
= "VR Session"
314 def draw(self
, context
):
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...
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])
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'
345 def poll(cls
, context
):
346 return not bpy
.app
.build_options
.xr_openxr
348 def draw(self
, context
):
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
365 # select newly created set
366 scene
.vr_landmarks_selected
= len(landmarks
) - 1
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'}
378 def poll(cls
, context
):
381 vl_objects
= bpy
.context
.view_layer
.objects
382 if vl_objects
.active
and vl_objects
.active
.type == 'CAMERA':
386 def execute(self
, context
):
387 scene
= context
.scene
388 landmarks
= scene
.vr_landmarks
389 cam
= context
.view_layer
.objects
.active
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
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'}
408 def poll(cls
, context
):
409 return bpy
.types
.XrSessionState
.is_running(context
)
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
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
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'}
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
)
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
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'}
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
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
520 lm_pos
= lm
.base_pose_location
521 scene
.cursor
.location
= lm_pos
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
):
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
)
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'}
555 def poll(cls
, context
):
556 return context
.scene
.camera
is not None
558 def execute(self
, context
):
561 scene
= context
.scene
562 lm
= VRLandmark
.get_selected_landmark(context
)
565 angle
= lm
.base_pose_angle
566 cam
.location
= lm
.base_pose_location
567 cam
.rotation_euler
= (math
.pi
/ 2, 0, angle
)
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'}
583 def execute(self
, context
):
584 scene
= context
.scene
586 if self
.index
>= len(scene
.vr_landmarks
):
589 scene
.vr_landmarks_active
= (
590 self
.index
if self
.properties
.is_property_set(
591 "index") else scene
.vr_landmarks_selected
597 class VIEW3D_PT_vr_viewport_feedback(Panel
):
598 bl_space_type
= 'VIEW_3D'
599 bl_region_type
= 'UI'
601 bl_label
= "Viewport Feedback"
602 bl_options
= {'DEFAULT_CLOSED'}
604 def draw(self
, context
):
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!")
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"
625 def draw(self
, context
):
628 if not hasattr(self
, "frame_shape"):
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
= (
639 frame_shape_verts
[0],
641 frame_shape_verts
[1],
643 frame_shape_verts
[2],
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)
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'}
669 def poll(cls
, context
):
670 view3d
= context
.space_data
672 view3d
.shading
.vr_show_virtual_camera
and
673 bpy
.types
.XrSessionState
.is_running(context
) and
674 not view3d
.mirror_xr_session
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)
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
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'}
714 def poll(cls
, context
):
715 view3d
= context
.space_data
717 view3d
.shading
.vr_show_landmarks
720 def setup(self
, context
):
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
734 if ((lm
.type == 'SCENE_CAMERA' and not scene
.camera
) or
735 (lm
.type == 'USER_CAMERA' and not lm
.base_pose_camera
)):
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
746 if lm
.type == '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
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
)
759 transmat
= Matrix
.Translation(lm
.base_pose_location
)
761 lm_mat
= transmat
@ rotmat
763 self
.gizmo
.matrix_basis
= lm_mat
767 VIEW3D_PT_vr_session
,
768 VIEW3D_PT_vr_session_view
,
769 VIEW3D_PT_vr_landmarks
,
770 VIEW3D_PT_vr_viewport_feedback
,
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
,
793 if not bpy
.app
.build_options
.xr_openxr
:
794 bpy
.utils
.register_class(VIEW3D_PT_vr_info
)
798 bpy
.utils
.register_class(cls
)
800 bpy
.types
.Scene
.vr_landmarks
= CollectionProperty(
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
)
823 if not bpy
.app
.build_options
.xr_openxr
:
824 bpy
.utils
.unregister_class(VIEW3D_PT_vr_info
)
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__":