mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-16 15:35:05 +00:00

This updates the VR Scene Inspection add-on with functionality for loading and using default controller actions, including controller poses and haptics, and visualizing VR controllers both in-headset and in the regular 3D viewport. In other words, users can finally view their VR controllers and use them to navigate their scene in VR. Controller actions (enabled by default) are available as an option in the "VR Session" panel. For controller bindings that require OpenXR extensions (Reverb G2, Vive Cosmos, Huawei), there is a new "Action Maps" panel where users can toggle these bindings. Bindings that require extensions are disabled by default since not all OpenXR runtimes may support them, which will lead to an error during action creation at session start. There is also an option in the "Action Maps" panel to use a gamepad (Xbox Controller) instead of motion controllers for VR actions/viewport navigation. In addition to default actions, this update adds new options for VR controller visualization. For in-headset (VR) visualization, controller visibility as well as style (dark/light, ray/no ray) can be set via the "View" panel. For visualization in the regular 3D viewport, there is a new option in the "Viewport Feedback" panel to draw controllers as gizmos, similar to the existing option for the VR camera (headset). Finally, this update also changes the VR Landmark "Custom Camera" type to "Custom Object", so users can specify any object (not just cameras) as a base pose reference, and adds a base scale option for custom object and custom pose-type landmarks. Reviewed By: Severin Differential Revision: https://developer.blender.org/D11271
245 lines
8.1 KiB
Python
245 lines
8.1 KiB
Python
# ##### 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)
|