Fix #100973: Node Wrangler: previewing node if hierarchy not active
[blender-addons.git] / camera_turnaround.py
blob2ffd7a2ba868d7784e9dc26f3529b25feb84a485
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "Turnaround Camera",
7 "author": "Antonio Vazquez (antonioya)",
8 "version": (0, 3, 0),
9 "blender": (2, 80, 0),
10 "location": "View3D > Sidebar > View Tab > Turnaround Camera",
11 "description": "Add a camera rotation around selected object",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/turnaround_camera.html",
13 "category": "Animation",
17 import bpy
18 from math import pi
19 from bpy.props import (
20 BoolProperty,
21 EnumProperty,
22 FloatProperty,
23 PointerProperty,
25 from bpy.types import (
26 Operator,
27 Panel,
28 PropertyGroup,
32 # ------------------------------------------------------
33 # Action class
34 # ------------------------------------------------------
35 class CAMERATURN_OT_RunAction(Operator):
36 bl_idname = "object.rotate_around"
37 bl_label = "Turnaround"
38 bl_description = "Create camera rotation around selected object"
39 bl_options = {'REGISTER', 'UNDO'}
41 def execute(self, context):
42 # ----------------------
43 # Save old data
44 # ----------------------
45 scene = context.scene
46 turn_camera = scene.turn_camera
47 selectobject = context.active_object
48 camera = scene.camera
49 savedcursor = scene.cursor.location.copy() # cursor position
50 savedframe = scene.frame_current
51 if turn_camera.use_cursor is False:
52 bpy.ops.view3d.snap_cursor_to_selected()
54 # -------------------------
55 # Create empty and parent
56 # -------------------------
57 bpy.ops.object.empty_add(type='PLAIN_AXES')
58 myempty = context.active_object
60 myempty.location = selectobject.location
61 savedstate = myempty.matrix_world
62 myempty.parent = selectobject
63 myempty.name = "MCH_Rotation_target"
64 myempty.matrix_world = savedstate
66 # -------------------------
67 # Parent camera to empty
68 # -------------------------
69 savedstate = camera.matrix_world
70 camera.parent = myempty
71 camera.matrix_world = savedstate
73 # -------------------------
74 # Now add revolutions
75 # (make empty active object)
76 # -------------------------
77 bpy.ops.object.select_all(False)
78 myempty.select_set(True)
79 context.view_layer.objects.active = myempty
80 # save current configuration
81 savedinterpolation = context.preferences.edit.keyframe_new_interpolation_type
82 # change interpolation mode
83 context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR'
84 # create first frame
85 myempty.rotation_euler = (0, 0, 0)
86 myempty.empty_display_size = 0.1
87 scene.frame_set(scene.frame_start)
88 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_start)
90 # Clear the Camera Animations if the option is checked
91 if turn_camera.reset_cam_anim:
92 try:
93 if bpy.data.cameras[camera.name].animation_data:
94 bpy.data.cameras[camera.name].animation_data_clear()
95 except Exception as e:
96 print("\n[Camera Turnaround]\nWarning: {}\n".format(e))
98 # Dolly zoom
99 if turn_camera.dolly_zoom != "0":
100 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens
101 bpy.data.cameras[camera.name].keyframe_insert("lens", frame=scene.frame_start)
103 # Calculate rotation XYZ
104 ix = -1 if turn_camera.inverse_x else 1
105 iy = -1 if turn_camera.inverse_y else 1
106 iz = -1 if turn_camera.inverse_z else 1
108 xrot = (pi * 2) * turn_camera.camera_revol_x * ix
109 yrot = (pi * 2) * turn_camera.camera_revol_y * iy
110 zrot = (pi * 2) * turn_camera.camera_revol_z * iz
112 # create middle frame
113 if turn_camera.back_forw is True:
114 myempty.rotation_euler = (xrot, yrot, zrot)
115 myempty.keyframe_insert(
116 data_path="rotation_euler",
117 frame=((scene.frame_end - scene.frame_start) / 2)
119 # reverse
120 xrot *= -1
121 yrot *= -1
122 zrot = 0
124 # Dolly zoom
125 if turn_camera.dolly_zoom == "2":
126 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens
127 bpy.data.cameras[camera.name].keyframe_insert(
128 "lens",
129 frame=((scene.frame_end - scene.frame_start) / 2)
132 # create last frame
133 myempty.rotation_euler = (xrot, yrot, zrot)
134 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_end)
135 # Dolly zoom
136 if turn_camera.dolly_zoom != "0":
137 if turn_camera.dolly_zoom == "1":
138 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens # final
139 else:
140 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens # back to init
142 bpy.data.cameras[camera.name].keyframe_insert(
143 "lens", frame=scene.frame_end
146 # Track constraint
147 if turn_camera.track is True:
148 bpy.context.view_layer.objects.active = camera
149 bpy.ops.object.constraint_add(type='TRACK_TO')
150 bpy.context.object.constraints[-1].track_axis = 'TRACK_NEGATIVE_Z'
151 bpy.context.object.constraints[-1].up_axis = 'UP_Y'
152 bpy.context.object.constraints[-1].target = myempty
154 # back previous configuration
155 context.preferences.edit.keyframe_new_interpolation_type = savedinterpolation
156 scene.cursor.location = savedcursor
158 # -------------------------
159 # Back to old selection
160 # -------------------------
161 bpy.ops.object.select_all(False)
162 selectobject.select_set(True)
163 bpy.context.view_layer.objects.active = selectobject
164 scene.frame_set(savedframe)
166 return {'FINISHED'}
169 # ------------------------------------------------------
170 # Define Properties
171 # ------------------------------------------------------
172 class CAMERATURN_Props(PropertyGroup):
174 camera_revol_x: FloatProperty(
175 name="X", min=0, max=25,
176 default=0, precision=2,
177 description="Number total of revolutions in X axis"
179 camera_revol_y: FloatProperty(
180 name="Y", min=0, max=25,
181 default=0, precision=2,
182 description="Number total of revolutions in Y axis"
184 camera_revol_z: FloatProperty(
185 name="Z", min=0, max=25,
186 default=1, precision=2,
187 description="Number total of revolutions in Z axis"
189 inverse_x: BoolProperty(
190 name="-X",
191 description="Inverse rotation",
192 default=False
194 inverse_y: BoolProperty(
195 name="-Y",
196 description="Inverse rotation",
197 default=False
199 inverse_z: BoolProperty(
200 name="-Z",
201 description="Inverse rotation",
202 default=False
204 use_cursor: BoolProperty(
205 name="Use cursor position",
206 description="Use cursor position instead of object origin",
207 default=False
209 back_forw: BoolProperty(
210 name="Back and forward",
211 description="Create back and forward animation",
212 default=False
214 dolly_zoom: EnumProperty(
215 items=(
216 ('0', "None", ""),
217 ('1', "Dolly zoom", ""),
218 ('2', "Dolly zoom B/F", "")
220 name="Lens Effects",
221 description="Create a camera lens movement"
223 camera_from_lens: FloatProperty(
224 name="From",
225 min=1, max=500, default=35,
226 precision=3,
227 description="Start lens value"
229 camera_to_lens: FloatProperty(
230 name="To",
231 min=1, max=500,
232 default=35, precision=3,
233 description="End lens value"
235 track: BoolProperty(
236 name="Create track constraint",
237 description="Add a track constraint to the camera",
238 default=False
240 reset_cam_anim: BoolProperty(
241 name="Clear Camera",
242 description="Clear previous camera animations if there are any\n"
243 "(For instance, previous Dolly Zoom)",
244 default=False
248 # ------------------------------------------------------
249 # UI Class
250 # ------------------------------------------------------
251 class CAMERATURN_PT_ui(Panel):
252 bl_idname = "CAMERA_TURN_PT_main"
253 bl_label = "Turnaround Camera"
254 bl_space_type = "VIEW_3D"
255 bl_region_type = "UI"
256 bl_category = "Animate"
257 bl_context = "objectmode"
258 bl_options = {'DEFAULT_CLOSED'}
260 def draw(self, context):
261 layout = self.layout
262 scene = context.scene
263 turn_camera = scene.turn_camera
265 try:
266 scene.camera.name
267 except AttributeError:
268 row = layout.row(align=False)
269 row.label(text="No defined camera for scene", icon="INFO")
270 return
272 if context.active_object is not None:
273 if context.active_object.type != 'CAMERA':
274 buf = context.active_object.name
275 row = layout.row(align=True)
276 row.operator("object.rotate_around", icon='OUTLINER_DATA_CAMERA')
277 box = row.box()
278 box.scale_y = 0.5
279 box.label(text=buf, icon='MESH_DATA')
280 row = layout.row(align=False)
281 row.prop(scene, "camera")
283 layout.label(text="Rotation:")
284 row = layout.row(align=True)
285 row.prop(scene, "frame_start")
286 row.prop(scene, "frame_end")
288 col = layout.column(align=True)
289 split = col.split(factor=0.85, align=True)
290 split.prop(turn_camera, "camera_revol_x")
291 split.prop(turn_camera, "inverse_x", toggle=True)
292 split = col.split(factor=0.85, align=True)
293 split.prop(turn_camera, "camera_revol_y")
294 split.prop(turn_camera, "inverse_y", toggle=True)
295 split = col.split(factor=0.85, align=True)
296 split.prop(turn_camera, "camera_revol_z")
297 split.prop(turn_camera, "inverse_z", toggle=True)
299 col = layout.column(align=True)
300 col.label(text="Options:")
301 row = col.row(align=True)
302 row.prop(turn_camera, "back_forw", toggle=True)
303 row.prop(turn_camera, "reset_cam_anim", toggle=True)
304 col.prop(turn_camera, "track", toggle=True)
305 col.prop(turn_camera, "use_cursor", toggle=True)
307 row = layout.row()
308 row.prop(turn_camera, "dolly_zoom")
309 if turn_camera.dolly_zoom != "0":
310 row = layout.row(align=True)
311 row.prop(turn_camera, "camera_from_lens")
312 row.prop(turn_camera, "camera_to_lens")
314 else:
315 buf = "No valid object selected"
316 layout.label(text=buf, icon='MESH_DATA')
319 # ------------------------------------------------------
320 # Registration
321 # ------------------------------------------------------
322 classes = (
323 CAMERATURN_OT_RunAction,
324 CAMERATURN_PT_ui,
325 CAMERATURN_Props
329 def register():
330 from bpy.utils import register_class
331 for cls in classes:
332 register_class(cls)
334 bpy.types.Scene.turn_camera = PointerProperty(type=CAMERATURN_Props)
337 def unregister():
338 from bpy.utils import unregister_class
339 for cls in reversed(classes):
340 unregister_class(cls)
342 del bpy.types.Scene.turn_camera
345 if __name__ == "__main__":
346 register()