mirror of
https://github.com/blender/blender-addons.git
synced 2025-07-23 00:48:26 +00:00
840 lines
26 KiB
Python
840 lines
26 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 (
|
|
Gizmo,
|
|
GizmoGroup,
|
|
PropertyGroup,
|
|
UIList,
|
|
Menu,
|
|
Panel,
|
|
Operator,
|
|
)
|
|
from bpy.props import (
|
|
CollectionProperty,
|
|
IntProperty,
|
|
BoolProperty,
|
|
)
|
|
from bpy.app.handlers import persistent
|
|
|
|
bl_info = {
|
|
"name": "VR Scene Inspection",
|
|
"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)"),
|
|
"support": "OFFICIAL",
|
|
"warning": "This is an early, limited preview of in development "
|
|
"VR support for Blender.",
|
|
"doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/vr_scene_inspection.html",
|
|
"category": "3D View",
|
|
}
|
|
|
|
|
|
@persistent
|
|
def ensure_default_vr_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 xr_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 == 'USER_CAMERA':
|
|
session_settings.base_pose_type = 'OBJECT'
|
|
elif landmark_active.type == 'CUSTOM':
|
|
session_settings.base_pose_type = 'CUSTOM'
|
|
|
|
|
|
def xr_landmark_active_camera_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_camera
|
|
|
|
|
|
def xr_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 xr_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 xr_landmark_type_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:
|
|
xr_landmark_active_type_update(self, context)
|
|
|
|
|
|
def xr_landmark_camera_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:
|
|
xr_landmark_active_camera_update(self, context)
|
|
|
|
|
|
def xr_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:
|
|
xr_landmark_active_base_pose_location_update(self, context)
|
|
|
|
|
|
def xr_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:
|
|
xr_landmark_active_base_pose_angle_update(self, context)
|
|
|
|
|
|
def xr_landmark_camera_object_poll(self, object):
|
|
return object.type == 'CAMERA'
|
|
|
|
|
|
def xr_landmark_active_update(self, context):
|
|
wm = context.window_manager
|
|
|
|
xr_landmark_active_type_update(self, context)
|
|
xr_landmark_active_camera_update(self, context)
|
|
xr_landmark_active_base_pose_location_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)
|
|
|
|
|
|
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"
|
|
)
|
|
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"),
|
|
('USER_CAMERA', "Custom Camera",
|
|
"Use an existing camera 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=xr_landmark_type_update,
|
|
)
|
|
base_pose_camera: bpy.props.PointerProperty(
|
|
name="Camera",
|
|
type=bpy.types.Object,
|
|
poll=xr_landmark_camera_object_poll,
|
|
update=xr_landmark_camera_update,
|
|
)
|
|
base_pose_location: bpy.props.FloatVectorProperty(
|
|
name="Base Pose Location",
|
|
subtype='TRANSLATION',
|
|
update=xr_landmark_base_pose_location_update,
|
|
)
|
|
|
|
base_pose_angle: bpy.props.FloatProperty(
|
|
name="Base Pose Angle",
|
|
subtype='ANGLE',
|
|
update=xr_landmark_base_pose_angle_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]
|
|
)
|
|
|
|
|
|
class VIEW3D_UL_vr_landmarks(UIList):
|
|
def draw_item(self, context, layout, _data, item, icon, _active_data,
|
|
_active_propname, index):
|
|
landmark = item
|
|
landmark_active_idx = context.scene.vr_landmarks_active
|
|
|
|
layout.emboss = 'NONE'
|
|
|
|
layout.prop(landmark, "name", text="")
|
|
|
|
icon = (
|
|
'RADIOBUT_ON' if (index == landmark_active_idx) else 'RADIOBUT_OFF'
|
|
)
|
|
props = layout.operator(
|
|
"view3d.vr_landmark_activate", text="", icon=icon)
|
|
props.index = index
|
|
|
|
|
|
class VIEW3D_PT_vr_landmarks(Panel):
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "VR"
|
|
bl_label = "Landmarks"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
scene = context.scene
|
|
landmark_selected = VRLandmark.get_selected_landmark(context)
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False # No animation.
|
|
|
|
row = layout.row()
|
|
|
|
row.template_list("VIEW3D_UL_vr_landmarks", "", scene, "vr_landmarks",
|
|
scene, "vr_landmarks_selected", rows=3)
|
|
|
|
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")
|
|
|
|
|
|
class VIEW3D_PT_vr_session_view(Panel):
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "VR"
|
|
bl_label = "View"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
session_settings = context.window_manager.xr_session_settings
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False # No animation.
|
|
|
|
col = layout.column(align=True, heading="Show")
|
|
col.prop(session_settings, "show_floor", text="Floor")
|
|
col.prop(session_settings, "show_annotation", text="Annotations")
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(session_settings, "clip_start", text="Clip Start")
|
|
col.prop(session_settings, "clip_end", text="End")
|
|
|
|
|
|
class VIEW3D_PT_vr_session(Panel):
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "VR"
|
|
bl_label = "VR Session"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
session_settings = context.window_manager.xr_session_settings
|
|
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False # No animation.
|
|
|
|
is_session_running = bpy.types.XrSessionState.is_running(context)
|
|
|
|
# Using SNAP_FACE because it looks like a stop icon -- I shouldn't
|
|
# have commit rights...
|
|
toggle_info = (
|
|
("Start VR Session", 'PLAY') if not is_session_running else (
|
|
"Stop VR Session", 'SNAP_FACE')
|
|
)
|
|
layout.operator("wm.xr_session_toggle",
|
|
text=toggle_info[0], icon=toggle_info[1])
|
|
|
|
layout.separator()
|
|
|
|
layout.prop(session_settings, "use_positional_tracking")
|
|
|
|
|
|
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"
|
|
bl_options = {'UNDO', 'REGISTER'}
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
landmarks = scene.vr_landmarks
|
|
|
|
landmarks.add()
|
|
|
|
# select newly created set
|
|
scene.vr_landmarks_selected = len(landmarks) - 1
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
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)
|
|
|
|
@staticmethod
|
|
def _calc_landmark_angle_from_viewer_rotation(rot):
|
|
from mathutils import Vector
|
|
|
|
# We want an angle around Z based on the current viewer rotation. Idea
|
|
# is to create a vector from the viewer rotation, project that onto a
|
|
# Z-Up plane and use the resulting vector to get an angle around Z.
|
|
|
|
view_rot_vec = Vector((0, 0, 1))
|
|
view_rot_vec.rotate(rot)
|
|
angle_vec = view_rot_vec - view_rot_vec.project(Vector((0, 0, 1)))
|
|
|
|
# We could probably use a 3D version of Vector.angle_signed() here, but
|
|
# that's not available. So manually calculate it via a quaternion delta.
|
|
forward_vec = Vector((0, -1, 0))
|
|
diff = angle_vec.rotation_difference(forward_vec)
|
|
|
|
return diff.angle * -diff.axis[2]
|
|
|
|
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
|
|
angle = self._calc_landmark_angle_from_viewer_rotation(rot)
|
|
|
|
lm.base_pose_location = loc
|
|
lm.base_pose_angle = angle
|
|
|
|
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"
|
|
bl_options = {'UNDO', 'REGISTER'}
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
landmarks = scene.vr_landmarks
|
|
|
|
if len(landmarks) > 1:
|
|
landmark_selected_idx = scene.vr_landmarks_selected
|
|
landmarks.remove(landmark_selected_idx)
|
|
|
|
scene.vr_landmarks_selected -= 1
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
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"
|
|
bl_options = {'UNDO', 'REGISTER'}
|
|
|
|
index: IntProperty(
|
|
name="Index",
|
|
options={'HIDDEN'},
|
|
)
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
|
|
if self.index >= len(scene.vr_landmarks):
|
|
return {'CANCELLED'}
|
|
|
|
scene.vr_landmarks_active = (
|
|
self.index if self.properties.is_property_set(
|
|
"index") else scene.vr_landmarks_selected
|
|
)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class VIEW3D_PT_vr_viewport_feedback(Panel):
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "VR"
|
|
bl_label = "Viewport Feedback"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
view3d = context.space_data
|
|
|
|
col = layout.column(align=True)
|
|
col.label(icon='ERROR', text="Note:")
|
|
col.label(text="Settings here may have a significant")
|
|
col.label(text="performance impact!")
|
|
|
|
layout.separator()
|
|
|
|
layout.prop(view3d.shading, "vr_show_virtual_camera")
|
|
layout.prop(view3d.shading, "vr_show_landmarks")
|
|
layout.prop(view3d, "mirror_xr_session")
|
|
|
|
|
|
class VIEW3D_GT_vr_camera_cone(Gizmo):
|
|
bl_idname = "VIEW_3D_GT_vr_camera_cone"
|
|
|
|
aspect = 1.0, 1.0
|
|
|
|
def draw(self, context):
|
|
import bgl
|
|
|
|
if not hasattr(self, "frame_shape"):
|
|
aspect = self.aspect
|
|
|
|
frame_shape_verts = (
|
|
(-aspect[0], -aspect[1], -1.0),
|
|
(aspect[0], -aspect[1], -1.0),
|
|
(aspect[0], aspect[1], -1.0),
|
|
(-aspect[0], aspect[1], -1.0),
|
|
)
|
|
lines_shape_verts = (
|
|
(0.0, 0.0, 0.0),
|
|
frame_shape_verts[0],
|
|
(0.0, 0.0, 0.0),
|
|
frame_shape_verts[1],
|
|
(0.0, 0.0, 0.0),
|
|
frame_shape_verts[2],
|
|
(0.0, 0.0, 0.0),
|
|
frame_shape_verts[3],
|
|
)
|
|
|
|
self.frame_shape = self.new_custom_shape(
|
|
'LINE_LOOP', frame_shape_verts)
|
|
self.lines_shape = self.new_custom_shape(
|
|
'LINES', lines_shape_verts)
|
|
|
|
# Ensure correct GL state (otherwise other gizmos might mess that up)
|
|
bgl.glLineWidth(1)
|
|
bgl.glEnable(bgl.GL_BLEND)
|
|
|
|
self.draw_custom_shape(self.frame_shape)
|
|
self.draw_custom_shape(self.lines_shape)
|
|
|
|
|
|
class VIEW3D_GGT_vr_viewer_pose(GizmoGroup):
|
|
bl_idname = "VIEW3D_GGT_vr_viewer_pose"
|
|
bl_label = "VR Viewer Pose Indicator"
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'WINDOW'
|
|
bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
view3d = context.space_data
|
|
return (
|
|
view3d.shading.vr_show_virtual_camera and
|
|
bpy.types.XrSessionState.is_running(context) and
|
|
not view3d.mirror_xr_session
|
|
)
|
|
|
|
@staticmethod
|
|
def _get_viewer_pose_matrix(context):
|
|
from mathutils import Matrix, Quaternion
|
|
|
|
wm = context.window_manager
|
|
|
|
loc = wm.xr_session_state.viewer_pose_location
|
|
rot = wm.xr_session_state.viewer_pose_rotation
|
|
|
|
rotmat = Matrix.Identity(3)
|
|
rotmat.rotate(rot)
|
|
rotmat.resize_4x4()
|
|
transmat = Matrix.Translation(loc)
|
|
|
|
return transmat @ rotmat
|
|
|
|
def setup(self, context):
|
|
gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname)
|
|
gizmo.aspect = 1 / 3, 1 / 4
|
|
|
|
gizmo.color = gizmo.color_highlight = 0.2, 0.6, 1.0
|
|
gizmo.alpha = 1.0
|
|
|
|
self.gizmo = gizmo
|
|
|
|
def draw_prepare(self, context):
|
|
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,
|
|
VIEW3D_PT_vr_landmarks,
|
|
VIEW3D_PT_vr_viewport_feedback,
|
|
|
|
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:
|
|
bpy.utils.register_class(cls)
|
|
|
|
bpy.types.Scene.vr_landmarks = CollectionProperty(
|
|
name="Landmark",
|
|
type=VRLandmark,
|
|
)
|
|
bpy.types.Scene.vr_landmarks_selected = IntProperty(
|
|
name="Selected Landmark"
|
|
)
|
|
bpy.types.Scene.vr_landmarks_active = IntProperty(
|
|
update=xr_landmark_active_update,
|
|
)
|
|
# View3DShading is the only per 3D-View struct with custom property
|
|
# support, so "abusing" that to get a per 3D-View option.
|
|
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:
|
|
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
|
|
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)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|