Cleanup: remove "Tweak" event type
[blender-addons.git] / camera_turnaround.py
blob471dfed308b5d2a5c509bb615db33ef076773034
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "Turnaround Camera",
5 "author": "Antonio Vazquez (antonioya)",
6 "version": (0, 3, 0),
7 "blender": (2, 80, 0),
8 "location": "View3D > Sidebar > View Tab > Turnaround Camera",
9 "description": "Add a camera rotation around selected object",
10 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/turnaround_camera.html",
11 "category": "Animation",
15 import bpy
16 from math import pi
17 from bpy.props import (
18 BoolProperty,
19 EnumProperty,
20 FloatProperty,
21 PointerProperty,
23 from bpy.types import (
24 Operator,
25 Panel,
26 PropertyGroup,
30 # ------------------------------------------------------
31 # Action class
32 # ------------------------------------------------------
33 class CAMERATURN_OT_RunAction(Operator):
34 bl_idname = "object.rotate_around"
35 bl_label = "Turnaround"
36 bl_description = "Create camera rotation around selected object"
37 bl_options = {'REGISTER', 'UNDO'}
39 def execute(self, context):
40 # ----------------------
41 # Save old data
42 # ----------------------
43 scene = context.scene
44 turn_camera = scene.turn_camera
45 selectobject = context.active_object
46 camera = scene.camera
47 savedcursor = scene.cursor.location.copy() # cursor position
48 savedframe = scene.frame_current
49 if turn_camera.use_cursor is False:
50 bpy.ops.view3d.snap_cursor_to_selected()
52 # -------------------------
53 # Create empty and parent
54 # -------------------------
55 bpy.ops.object.empty_add(type='PLAIN_AXES')
56 myempty = context.active_object
58 myempty.location = selectobject.location
59 savedstate = myempty.matrix_world
60 myempty.parent = selectobject
61 myempty.name = "MCH_Rotation_target"
62 myempty.matrix_world = savedstate
64 # -------------------------
65 # Parent camera to empty
66 # -------------------------
67 savedstate = camera.matrix_world
68 camera.parent = myempty
69 camera.matrix_world = savedstate
71 # -------------------------
72 # Now add revolutions
73 # (make empty active object)
74 # -------------------------
75 bpy.ops.object.select_all(False)
76 myempty.select_set(True)
77 context.view_layer.objects.active = myempty
78 # save current configuration
79 savedinterpolation = context.preferences.edit.keyframe_new_interpolation_type
80 # change interpolation mode
81 context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR'
82 # create first frame
83 myempty.rotation_euler = (0, 0, 0)
84 myempty.empty_display_size = 0.1
85 scene.frame_set(scene.frame_start)
86 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_start)
88 # Clear the Camera Animations if the option is checked
89 if turn_camera.reset_cam_anim:
90 try:
91 if bpy.data.cameras[camera.name].animation_data:
92 bpy.data.cameras[camera.name].animation_data_clear()
93 except Exception as e:
94 print("\n[Camera Turnaround]\nWarning: {}\n".format(e))
96 # Dolly zoom
97 if turn_camera.dolly_zoom != "0":
98 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens
99 bpy.data.cameras[camera.name].keyframe_insert("lens", frame=scene.frame_start)
101 # Calculate rotation XYZ
102 ix = -1 if turn_camera.inverse_x else 1
103 iy = -1 if turn_camera.inverse_y else 1
104 iz = -1 if turn_camera.inverse_z else 1
106 xrot = (pi * 2) * turn_camera.camera_revol_x * ix
107 yrot = (pi * 2) * turn_camera.camera_revol_y * iy
108 zrot = (pi * 2) * turn_camera.camera_revol_z * iz
110 # create middle frame
111 if turn_camera.back_forw is True:
112 myempty.rotation_euler = (xrot, yrot, zrot)
113 myempty.keyframe_insert(
114 data_path="rotation_euler",
115 frame=((scene.frame_end - scene.frame_start) / 2)
117 # reverse
118 xrot *= -1
119 yrot *= -1
120 zrot = 0
122 # Dolly zoom
123 if turn_camera.dolly_zoom == "2":
124 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens
125 bpy.data.cameras[camera.name].keyframe_insert(
126 "lens",
127 frame=((scene.frame_end - scene.frame_start) / 2)
130 # create last frame
131 myempty.rotation_euler = (xrot, yrot, zrot)
132 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_end)
133 # Dolly zoom
134 if turn_camera.dolly_zoom != "0":
135 if turn_camera.dolly_zoom == "1":
136 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens # final
137 else:
138 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens # back to init
140 bpy.data.cameras[camera.name].keyframe_insert(
141 "lens", frame=scene.frame_end
144 # Track constraint
145 if turn_camera.track is True:
146 bpy.context.view_layer.objects.active = camera
147 bpy.ops.object.constraint_add(type='TRACK_TO')
148 bpy.context.object.constraints[-1].track_axis = 'TRACK_NEGATIVE_Z'
149 bpy.context.object.constraints[-1].up_axis = 'UP_Y'
150 bpy.context.object.constraints[-1].target = myempty
152 # back previous configuration
153 context.preferences.edit.keyframe_new_interpolation_type = savedinterpolation
154 scene.cursor.location = savedcursor
156 # -------------------------
157 # Back to old selection
158 # -------------------------
159 bpy.ops.object.select_all(False)
160 selectobject.select_set(True)
161 bpy.context.view_layer.objects.active = selectobject
162 scene.frame_set(savedframe)
164 return {'FINISHED'}
167 # ------------------------------------------------------
168 # Define Properties
169 # ------------------------------------------------------
170 class CAMERATURN_Props(PropertyGroup):
172 camera_revol_x: FloatProperty(
173 name="X", min=0, max=25,
174 default=0, precision=2,
175 description="Number total of revolutions in X axis"
177 camera_revol_y: FloatProperty(
178 name="Y", min=0, max=25,
179 default=0, precision=2,
180 description="Number total of revolutions in Y axis"
182 camera_revol_z: FloatProperty(
183 name="Z", min=0, max=25,
184 default=1, precision=2,
185 description="Number total of revolutions in Z axis"
187 inverse_x: BoolProperty(
188 name="-X",
189 description="Inverse rotation",
190 default=False
192 inverse_y: BoolProperty(
193 name="-Y",
194 description="Inverse rotation",
195 default=False
197 inverse_z: BoolProperty(
198 name="-Z",
199 description="Inverse rotation",
200 default=False
202 use_cursor: BoolProperty(
203 name="Use cursor position",
204 description="Use cursor position instead of object origin",
205 default=False
207 back_forw: BoolProperty(
208 name="Back and forward",
209 description="Create back and forward animation",
210 default=False
212 dolly_zoom: EnumProperty(
213 items=(
214 ('0', "None", ""),
215 ('1', "Dolly zoom", ""),
216 ('2', "Dolly zoom B/F", "")
218 name="Lens Effects",
219 description="Create a camera lens movement"
221 camera_from_lens: FloatProperty(
222 name="From",
223 min=1, max=500, default=35,
224 precision=3,
225 description="Start lens value"
227 camera_to_lens: FloatProperty(
228 name="To",
229 min=1, max=500,
230 default=35, precision=3,
231 description="End lens value"
233 track: BoolProperty(
234 name="Create track constraint",
235 description="Add a track constraint to the camera",
236 default=False
238 reset_cam_anim: BoolProperty(
239 name="Clear Camera",
240 description="Clear previous camera animations if there are any\n"
241 "(For instance, previous Dolly Zoom)",
242 default=False
246 # ------------------------------------------------------
247 # UI Class
248 # ------------------------------------------------------
249 class CAMERATURN_PT_ui(Panel):
250 bl_idname = "CAMERA_TURN_PT_main"
251 bl_label = "Turnaround Camera"
252 bl_space_type = "VIEW_3D"
253 bl_region_type = "UI"
254 bl_category = "Animate"
255 bl_context = "objectmode"
256 bl_options = {'DEFAULT_CLOSED'}
258 def draw(self, context):
259 layout = self.layout
260 scene = context.scene
261 turn_camera = scene.turn_camera
263 try:
264 scene.camera.name
265 except AttributeError:
266 row = layout.row(align=False)
267 row.label(text="No defined camera for scene", icon="INFO")
268 return
270 if context.active_object is not None:
271 if context.active_object.type != 'CAMERA':
272 buf = context.active_object.name
273 row = layout.row(align=True)
274 row.operator("object.rotate_around", icon='OUTLINER_DATA_CAMERA')
275 box = row.box()
276 box.scale_y = 0.5
277 box.label(text=buf, icon='MESH_DATA')
278 row = layout.row(align=False)
279 row.prop(scene, "camera")
281 layout.label(text="Rotation:")
282 row = layout.row(align=True)
283 row.prop(scene, "frame_start")
284 row.prop(scene, "frame_end")
286 col = layout.column(align=True)
287 split = col.split(factor=0.85, align=True)
288 split.prop(turn_camera, "camera_revol_x")
289 split.prop(turn_camera, "inverse_x", toggle=True)
290 split = col.split(factor=0.85, align=True)
291 split.prop(turn_camera, "camera_revol_y")
292 split.prop(turn_camera, "inverse_y", toggle=True)
293 split = col.split(factor=0.85, align=True)
294 split.prop(turn_camera, "camera_revol_z")
295 split.prop(turn_camera, "inverse_z", toggle=True)
297 col = layout.column(align=True)
298 col.label(text="Options:")
299 row = col.row(align=True)
300 row.prop(turn_camera, "back_forw", toggle=True)
301 row.prop(turn_camera, "reset_cam_anim", toggle=True)
302 col.prop(turn_camera, "track", toggle=True)
303 col.prop(turn_camera, "use_cursor", toggle=True)
305 row = layout.row()
306 row.prop(turn_camera, "dolly_zoom")
307 if turn_camera.dolly_zoom != "0":
308 row = layout.row(align=True)
309 row.prop(turn_camera, "camera_from_lens")
310 row.prop(turn_camera, "camera_to_lens")
312 else:
313 buf = "No valid object selected"
314 layout.label(text=buf, icon='MESH_DATA')
317 # ------------------------------------------------------
318 # Registration
319 # ------------------------------------------------------
320 classes = (
321 CAMERATURN_OT_RunAction,
322 CAMERATURN_PT_ui,
323 CAMERATURN_Props
327 def register():
328 from bpy.utils import register_class
329 for cls in classes:
330 register_class(cls)
332 bpy.types.Scene.turn_camera = PointerProperty(type=CAMERATURN_Props)
335 def unregister():
336 from bpy.utils import unregister_class
337 for cls in reversed(classes):
338 unregister_class(cls)
340 del bpy.types.Scene.turn_camera
343 if __name__ == "__main__":
344 register()