Sun Position: update translation
[blender-addons.git] / viewport_vr_preview / operators.py
blobd304f39b8cf4e8baa96472213a605c43763c7c45
1 # SPDX-FileCopyrightText: 2021-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 if "bpy" in locals():
6 import importlib
7 importlib.reload(properties)
8 else:
9 from . import properties
11 import bpy
12 import gpu
13 from bpy.app.translations import pgettext_data as data_
14 from bpy.types import (
15 Gizmo,
16 GizmoGroup,
17 Operator,
19 import math
20 from math import radians
21 from mathutils import Euler, Matrix, Quaternion, Vector
24 ### Landmarks.
25 class VIEW3D_OT_vr_landmark_add(Operator):
26 bl_idname = "view3d.vr_landmark_add"
27 bl_label = "Add VR Landmark"
28 bl_description = "Add a new VR landmark to the list and select it"
29 bl_options = {'UNDO', 'REGISTER'}
31 def execute(self, context):
32 scene = context.scene
33 landmarks = scene.vr_landmarks
35 landmarks.add()
37 # select newly created set
38 scene.vr_landmarks_selected = len(landmarks) - 1
40 return {'FINISHED'}
43 class VIEW3D_OT_vr_landmark_from_camera(Operator):
44 bl_idname = "view3d.vr_landmark_from_camera"
45 bl_label = "Add VR Landmark from Camera"
46 bl_description = "Add a new VR landmark from the active camera object to the list and select it"
47 bl_options = {'UNDO', 'REGISTER'}
49 @classmethod
50 def poll(cls, context):
51 cam_selected = False
53 vl_objects = bpy.context.view_layer.objects
54 if vl_objects.active and vl_objects.active.type == 'CAMERA':
55 cam_selected = True
56 return cam_selected
58 def execute(self, context):
59 scene = context.scene
60 landmarks = scene.vr_landmarks
61 cam = context.view_layer.objects.active
62 lm = landmarks.add()
63 lm.type = 'OBJECT'
64 lm.base_pose_object = cam
65 lm.name = "LM_" + cam.name
67 # select newly created set
68 scene.vr_landmarks_selected = len(landmarks) - 1
70 return {'FINISHED'}
73 class VIEW3D_OT_vr_landmark_from_session(Operator):
74 bl_idname = "view3d.vr_landmark_from_session"
75 bl_label = "Add VR Landmark from Session"
76 bl_description = "Add VR landmark from the viewer pose of the running VR session to the list and select it"
77 bl_options = {'UNDO', 'REGISTER'}
79 @classmethod
80 def poll(cls, context):
81 return bpy.types.XrSessionState.is_running(context)
83 def execute(self, context):
84 scene = context.scene
85 landmarks = scene.vr_landmarks
86 wm = context.window_manager
88 lm = landmarks.add()
89 lm.type = "CUSTOM"
90 scene.vr_landmarks_selected = len(landmarks) - 1
92 loc = wm.xr_session_state.viewer_pose_location
93 rot = wm.xr_session_state.viewer_pose_rotation.to_euler()
95 lm.base_pose_location = loc
96 lm.base_pose_angle = rot[2]
98 return {'FINISHED'}
101 class VIEW3D_OT_vr_camera_landmark_from_session(Operator):
102 bl_idname = "view3d.vr_camera_landmark_from_session"
103 bl_label = "Add Camera and VR Landmark from Session"
104 bl_description = "Create a new Camera and VR Landmark from the viewer pose of the running VR session and select it"
105 bl_options = {'UNDO', 'REGISTER'}
107 @classmethod
108 def poll(cls, context):
109 return bpy.types.XrSessionState.is_running(context)
111 def execute(self, context):
112 scene = context.scene
113 landmarks = scene.vr_landmarks
114 wm = context.window_manager
116 lm = landmarks.add()
117 lm.type = 'OBJECT'
118 scene.vr_landmarks_selected = len(landmarks) - 1
120 loc = wm.xr_session_state.viewer_pose_location
121 rot = wm.xr_session_state.viewer_pose_rotation.to_euler()
123 cam = bpy.data.cameras.new(data_("Camera") + "_" + lm.name)
124 new_cam = bpy.data.objects.new(data_("Camera") + "_" + lm.name, cam)
125 scene.collection.objects.link(new_cam)
126 new_cam.location = loc
127 new_cam.rotation_euler = rot
129 lm.base_pose_object = new_cam
131 return {'FINISHED'}
134 class VIEW3D_OT_update_vr_landmark(Operator):
135 bl_idname = "view3d.update_vr_landmark"
136 bl_label = "Update Custom VR Landmark"
137 bl_description = "Update the selected landmark from the current viewer pose in the VR session"
138 bl_options = {'UNDO', 'REGISTER'}
140 @classmethod
141 def poll(cls, context):
142 selected_landmark = properties.VRLandmark.get_selected_landmark(context)
143 return bpy.types.XrSessionState.is_running(context) and selected_landmark.type == 'CUSTOM'
145 def execute(self, context):
146 wm = context.window_manager
148 lm = properties.VRLandmark.get_selected_landmark(context)
150 loc = wm.xr_session_state.viewer_pose_location
151 rot = wm.xr_session_state.viewer_pose_rotation.to_euler()
153 lm.base_pose_location = loc
154 lm.base_pose_angle = rot
156 # Re-activate the landmark to trigger viewer reset and flush landmark settings to the session settings.
157 properties.vr_landmark_active_update(None, context)
159 return {'FINISHED'}
162 class VIEW3D_OT_vr_landmark_remove(Operator):
163 bl_idname = "view3d.vr_landmark_remove"
164 bl_label = "Remove VR Landmark"
165 bl_description = "Delete the selected VR landmark from the list"
166 bl_options = {'UNDO', 'REGISTER'}
168 def execute(self, context):
169 scene = context.scene
170 landmarks = scene.vr_landmarks
172 if len(landmarks) > 1:
173 landmark_selected_idx = scene.vr_landmarks_selected
174 landmarks.remove(landmark_selected_idx)
176 scene.vr_landmarks_selected -= 1
178 return {'FINISHED'}
181 class VIEW3D_OT_cursor_to_vr_landmark(Operator):
182 bl_idname = "view3d.cursor_to_vr_landmark"
183 bl_label = "Cursor to VR Landmark"
184 bl_description = "Move the 3D Cursor to the selected VR Landmark"
185 bl_options = {'UNDO', 'REGISTER'}
187 @classmethod
188 def poll(cls, context):
189 lm = properties.VRLandmark.get_selected_landmark(context)
190 if lm.type == 'SCENE_CAMERA':
191 return context.scene.camera is not None
192 elif lm.type == 'OBJECT':
193 return lm.base_pose_object is not None
195 return True
197 def execute(self, context):
198 scene = context.scene
199 lm = properties.VRLandmark.get_selected_landmark(context)
200 if lm.type == 'SCENE_CAMERA':
201 lm_pos = scene.camera.location
202 elif lm.type == 'OBJECT':
203 lm_pos = lm.base_pose_object.location
204 else:
205 lm_pos = lm.base_pose_location
206 scene.cursor.location = lm_pos
208 return{'FINISHED'}
211 class VIEW3D_OT_add_camera_from_vr_landmark(Operator):
212 bl_idname = "view3d.add_camera_from_vr_landmark"
213 bl_label = "New Camera from VR Landmark"
214 bl_description = "Create a new Camera from the selected VR Landmark"
215 bl_options = {'UNDO', 'REGISTER'}
217 def execute(self, context):
218 scene = context.scene
219 lm = properties.VRLandmark.get_selected_landmark(context)
221 cam = bpy.data.cameras.new(data_("Camera") + "_" + lm.name)
222 new_cam = bpy.data.objects.new(data_("Camera") + "_" + lm.name, cam)
223 scene.collection.objects.link(new_cam)
224 angle = lm.base_pose_angle
225 new_cam.location = lm.base_pose_location
226 new_cam.rotation_euler = (math.pi / 2, 0, angle)
228 return {'FINISHED'}
231 class VIEW3D_OT_camera_to_vr_landmark(Operator):
232 bl_idname = "view3d.camera_to_vr_landmark"
233 bl_label = "Scene Camera to VR Landmark"
234 bl_description = "Position the scene camera at the selected landmark"
235 bl_options = {'UNDO', 'REGISTER'}
237 @classmethod
238 def poll(cls, context):
239 return context.scene.camera is not None
241 def execute(self, context):
242 scene = context.scene
243 lm = properties.VRLandmark.get_selected_landmark(context)
245 cam = scene.camera
246 angle = lm.base_pose_angle
247 cam.location = lm.base_pose_location
248 cam.rotation_euler = (math.pi / 2, 0, angle)
250 return {'FINISHED'}
253 class VIEW3D_OT_vr_landmark_activate(Operator):
254 bl_idname = "view3d.vr_landmark_activate"
255 bl_label = "Activate VR Landmark"
256 bl_description = "Change to the selected VR landmark from the list"
257 bl_options = {'UNDO', 'REGISTER'}
259 index: bpy.props.IntProperty(
260 name="Index",
261 options={'HIDDEN'},
264 def execute(self, context):
265 scene = context.scene
267 if self.index >= len(scene.vr_landmarks):
268 return {'CANCELLED'}
270 scene.vr_landmarks_active = (
271 self.index if self.properties.is_property_set(
272 "index") else scene.vr_landmarks_selected
275 return {'FINISHED'}
278 ### Gizmos.
279 class VIEW3D_GT_vr_camera_cone(Gizmo):
280 bl_idname = "VIEW_3D_GT_vr_camera_cone"
282 aspect = 1.0, 1.0
284 def draw(self, context):
285 if not hasattr(self, "frame_shape"):
286 aspect = self.aspect
288 frame_shape_verts = (
289 (-aspect[0], -aspect[1], -1.0),
290 (aspect[0], -aspect[1], -1.0),
291 (aspect[0], aspect[1], -1.0),
292 (-aspect[0], aspect[1], -1.0),
294 lines_shape_verts = (
295 (0.0, 0.0, 0.0),
296 frame_shape_verts[0],
297 (0.0, 0.0, 0.0),
298 frame_shape_verts[1],
299 (0.0, 0.0, 0.0),
300 frame_shape_verts[2],
301 (0.0, 0.0, 0.0),
302 frame_shape_verts[3],
305 self.frame_shape = self.new_custom_shape(
306 'LINE_LOOP', frame_shape_verts)
307 self.lines_shape = self.new_custom_shape(
308 'LINES', lines_shape_verts)
310 # Ensure correct GL state (otherwise other gizmos might mess that up)
311 gpu.state.line_width_set(1.0)
312 gpu.state.blend_set('ALPHA')
314 self.draw_custom_shape(self.frame_shape)
315 self.draw_custom_shape(self.lines_shape)
318 class VIEW3D_GT_vr_controller_grip(Gizmo):
319 bl_idname = "VIEW_3D_GT_vr_controller_grip"
321 def draw(self, context):
322 gpu.state.line_width_set(1.0)
323 gpu.state.blend_set('ALPHA')
325 self.color = 0.422, 0.438, 0.446
326 self.draw_preset_circle(self.matrix_basis, axis='POS_X')
327 self.draw_preset_circle(self.matrix_basis, axis='POS_Y')
328 self.draw_preset_circle(self.matrix_basis, axis='POS_Z')
331 class VIEW3D_GT_vr_controller_aim(Gizmo):
332 bl_idname = "VIEW_3D_GT_vr_controller_aim"
334 def draw(self, context):
335 gpu.state.line_width_set(1.0)
336 gpu.state.blend_set('ALPHA')
338 self.color = 1.0, 0.2, 0.322
339 self.draw_preset_arrow(self.matrix_basis, axis='POS_X')
340 self.color = 0.545, 0.863, 0.0
341 self.draw_preset_arrow(self.matrix_basis, axis='POS_Y')
342 self.color = 0.157, 0.565, 1.0
343 self.draw_preset_arrow(self.matrix_basis, axis='POS_Z')
346 class VIEW3D_GGT_vr_viewer_pose(GizmoGroup):
347 bl_idname = "VIEW3D_GGT_vr_viewer_pose"
348 bl_label = "VR Viewer Pose Indicator"
349 bl_space_type = 'VIEW_3D'
350 bl_region_type = 'WINDOW'
351 bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'}
353 @classmethod
354 def poll(cls, context):
355 view3d = context.space_data
356 return (
357 view3d.shading.vr_show_virtual_camera and
358 bpy.types.XrSessionState.is_running(context) and
359 not view3d.mirror_xr_session
362 @staticmethod
363 def _get_viewer_pose_matrix(context):
364 wm = context.window_manager
366 loc = wm.xr_session_state.viewer_pose_location
367 rot = wm.xr_session_state.viewer_pose_rotation
369 rotmat = Matrix.Identity(3)
370 rotmat.rotate(rot)
371 rotmat.resize_4x4()
372 transmat = Matrix.Translation(loc)
374 return transmat @ rotmat
376 def setup(self, context):
377 gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname)
378 gizmo.aspect = 1 / 3, 1 / 4
380 gizmo.color = gizmo.color_highlight = 0.2, 0.6, 1.0
381 gizmo.alpha = 1.0
383 self.gizmo = gizmo
385 def draw_prepare(self, context):
386 self.gizmo.matrix_basis = self._get_viewer_pose_matrix(context)
389 class VIEW3D_GGT_vr_controller_poses(GizmoGroup):
390 bl_idname = "VIEW3D_GGT_vr_controller_poses"
391 bl_label = "VR Controller Poses Indicator"
392 bl_space_type = 'VIEW_3D'
393 bl_region_type = 'WINDOW'
394 bl_options = {'3D', 'PERSISTENT', 'SCALE', 'VR_REDRAWS'}
396 @classmethod
397 def poll(cls, context):
398 view3d = context.space_data
399 return (
400 view3d.shading.vr_show_controllers and
401 bpy.types.XrSessionState.is_running(context) and
402 not view3d.mirror_xr_session
405 @staticmethod
406 def _get_controller_pose_matrix(context, idx, is_grip, scale):
407 wm = context.window_manager
409 loc = None
410 rot = None
411 if is_grip:
412 loc = wm.xr_session_state.controller_grip_location_get(context, idx)
413 rot = wm.xr_session_state.controller_grip_rotation_get(context, idx)
414 else:
415 loc = wm.xr_session_state.controller_aim_location_get(context, idx)
416 rot = wm.xr_session_state.controller_aim_rotation_get(context, idx)
418 rotmat = Matrix.Identity(3)
419 rotmat.rotate(Quaternion(Vector(rot)))
420 rotmat.resize_4x4()
421 transmat = Matrix.Translation(loc)
422 scalemat = Matrix.Scale(scale, 4)
424 return transmat @ rotmat @ scalemat
426 def setup(self, context):
427 for idx in range(2):
428 self.gizmos.new(VIEW3D_GT_vr_controller_grip.bl_idname)
429 self.gizmos.new(VIEW3D_GT_vr_controller_aim.bl_idname)
431 for gizmo in self.gizmos:
432 gizmo.aspect = 1 / 3, 1 / 4
433 gizmo.color_highlight = 1.0, 1.0, 1.0
434 gizmo.alpha = 1.0
436 def draw_prepare(self, context):
437 grip_idx = 0
438 aim_idx = 0
439 idx = 0
440 scale = 1.0
441 for gizmo in self.gizmos:
442 is_grip = (gizmo.bl_idname == VIEW3D_GT_vr_controller_grip.bl_idname)
443 if (is_grip):
444 idx = grip_idx
445 grip_idx += 1
446 scale = 0.1
447 else:
448 idx = aim_idx
449 aim_idx += 1
450 scale = 0.5
451 gizmo.matrix_basis = self._get_controller_pose_matrix(context, idx, is_grip, scale)
454 class VIEW3D_GGT_vr_landmarks(GizmoGroup):
455 bl_idname = "VIEW3D_GGT_vr_landmarks"
456 bl_label = "VR Landmark Indicators"
457 bl_space_type = 'VIEW_3D'
458 bl_region_type = 'WINDOW'
459 bl_options = {'3D', 'PERSISTENT', 'SCALE'}
461 @classmethod
462 def poll(cls, context):
463 view3d = context.space_data
464 return (
465 view3d.shading.vr_show_landmarks
468 def setup(self, context):
469 pass
471 def draw_prepare(self, context):
472 # first delete the old gizmos
473 for g in self.gizmos:
474 self.gizmos.remove(g)
476 scene = context.scene
477 landmarks = scene.vr_landmarks
479 for lm in landmarks:
480 if ((lm.type == 'SCENE_CAMERA' and not scene.camera) or
481 (lm.type == 'OBJECT' and not lm.base_pose_object)):
482 continue
484 gizmo = self.gizmos.new(VIEW3D_GT_vr_camera_cone.bl_idname)
485 gizmo.aspect = 1 / 3, 1 / 4
487 gizmo.color = gizmo.color_highlight = 0.2, 1.0, 0.6
488 gizmo.alpha = 1.0
490 self.gizmo = gizmo
492 if lm.type == 'SCENE_CAMERA':
493 cam = scene.camera
494 lm_mat = cam.matrix_world if cam else Matrix.Identity(4)
495 elif lm.type == 'OBJECT':
496 lm_mat = lm.base_pose_object.matrix_world
497 else:
498 angle = lm.base_pose_angle
499 raw_rot = Euler((radians(90.0), 0, angle))
501 rotmat = Matrix.Identity(3)
502 rotmat.rotate(raw_rot)
503 rotmat.resize_4x4()
505 transmat = Matrix.Translation(lm.base_pose_location)
507 lm_mat = transmat @ rotmat
509 self.gizmo.matrix_basis = lm_mat
512 classes = (
513 VIEW3D_OT_vr_landmark_add,
514 VIEW3D_OT_vr_landmark_remove,
515 VIEW3D_OT_vr_landmark_activate,
516 VIEW3D_OT_vr_landmark_from_session,
517 VIEW3D_OT_vr_camera_landmark_from_session,
518 VIEW3D_OT_add_camera_from_vr_landmark,
519 VIEW3D_OT_camera_to_vr_landmark,
520 VIEW3D_OT_vr_landmark_from_camera,
521 VIEW3D_OT_cursor_to_vr_landmark,
522 VIEW3D_OT_update_vr_landmark,
524 VIEW3D_GT_vr_camera_cone,
525 VIEW3D_GT_vr_controller_grip,
526 VIEW3D_GT_vr_controller_aim,
527 VIEW3D_GGT_vr_viewer_pose,
528 VIEW3D_GGT_vr_controller_poses,
529 VIEW3D_GGT_vr_landmarks,
533 def register():
534 for cls in classes:
535 bpy.utils.register_class(cls)
538 def unregister():
539 for cls in classes:
540 bpy.utils.unregister_class(cls)