# SPDX-License-Identifier: GPL-2.0-or-later 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)